1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "video/send_statistics_proxy.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <array>
15*d9f75844SAndroid Build Coastguard Worker #include <cmath>
16*d9f75844SAndroid Build Coastguard Worker #include <limits>
17*d9f75844SAndroid Build Coastguard Worker #include <utility>
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/match.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_codec_constants.h"
21*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_codec_type.h"
22*d9f75844SAndroid Build Coastguard Worker #include "api/video_codecs/video_codec.h"
23*d9f75844SAndroid Build Coastguard Worker #include "modules/video_coding/include/video_codec_interface.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/numerics/mod_ops.h"
27*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
28*d9f75844SAndroid Build Coastguard Worker #include "system_wrappers/include/metrics.h"
29*d9f75844SAndroid Build Coastguard Worker
30*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
31*d9f75844SAndroid Build Coastguard Worker namespace {
32*d9f75844SAndroid Build Coastguard Worker const float kEncodeTimeWeigthFactor = 0.5f;
33*d9f75844SAndroid Build Coastguard Worker const size_t kMaxEncodedFrameMapSize = 150;
34*d9f75844SAndroid Build Coastguard Worker const int64_t kMaxEncodedFrameWindowMs = 800;
35*d9f75844SAndroid Build Coastguard Worker const uint32_t kMaxEncodedFrameTimestampDiff = 900000; // 10 sec.
36*d9f75844SAndroid Build Coastguard Worker const int64_t kBucketSizeMs = 100;
37*d9f75844SAndroid Build Coastguard Worker const size_t kBucketCount = 10;
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker const char kVp8ForcedFallbackEncoderFieldTrial[] =
40*d9f75844SAndroid Build Coastguard Worker "WebRTC-VP8-Forced-Fallback-Encoder-v2";
41*d9f75844SAndroid Build Coastguard Worker const char kVp8SwCodecName[] = "libvpx";
42*d9f75844SAndroid Build Coastguard Worker
43*d9f75844SAndroid Build Coastguard Worker // Used by histograms. Values of entries should not be changed.
44*d9f75844SAndroid Build Coastguard Worker enum HistogramCodecType {
45*d9f75844SAndroid Build Coastguard Worker kVideoUnknown = 0,
46*d9f75844SAndroid Build Coastguard Worker kVideoVp8 = 1,
47*d9f75844SAndroid Build Coastguard Worker kVideoVp9 = 2,
48*d9f75844SAndroid Build Coastguard Worker kVideoH264 = 3,
49*d9f75844SAndroid Build Coastguard Worker kVideoAv1 = 4,
50*d9f75844SAndroid Build Coastguard Worker kVideoMax = 64,
51*d9f75844SAndroid Build Coastguard Worker };
52*d9f75844SAndroid Build Coastguard Worker
53*d9f75844SAndroid Build Coastguard Worker const char* kRealtimePrefix = "WebRTC.Video.";
54*d9f75844SAndroid Build Coastguard Worker const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
55*d9f75844SAndroid Build Coastguard Worker
GetUmaPrefix(VideoEncoderConfig::ContentType content_type)56*d9f75844SAndroid Build Coastguard Worker const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
57*d9f75844SAndroid Build Coastguard Worker switch (content_type) {
58*d9f75844SAndroid Build Coastguard Worker case VideoEncoderConfig::ContentType::kRealtimeVideo:
59*d9f75844SAndroid Build Coastguard Worker return kRealtimePrefix;
60*d9f75844SAndroid Build Coastguard Worker case VideoEncoderConfig::ContentType::kScreen:
61*d9f75844SAndroid Build Coastguard Worker return kScreenPrefix;
62*d9f75844SAndroid Build Coastguard Worker }
63*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
64*d9f75844SAndroid Build Coastguard Worker return nullptr;
65*d9f75844SAndroid Build Coastguard Worker }
66*d9f75844SAndroid Build Coastguard Worker
PayloadNameToHistogramCodecType(const std::string & payload_name)67*d9f75844SAndroid Build Coastguard Worker HistogramCodecType PayloadNameToHistogramCodecType(
68*d9f75844SAndroid Build Coastguard Worker const std::string& payload_name) {
69*d9f75844SAndroid Build Coastguard Worker VideoCodecType codecType = PayloadStringToCodecType(payload_name);
70*d9f75844SAndroid Build Coastguard Worker switch (codecType) {
71*d9f75844SAndroid Build Coastguard Worker case kVideoCodecVP8:
72*d9f75844SAndroid Build Coastguard Worker return kVideoVp8;
73*d9f75844SAndroid Build Coastguard Worker case kVideoCodecVP9:
74*d9f75844SAndroid Build Coastguard Worker return kVideoVp9;
75*d9f75844SAndroid Build Coastguard Worker case kVideoCodecH264:
76*d9f75844SAndroid Build Coastguard Worker return kVideoH264;
77*d9f75844SAndroid Build Coastguard Worker case kVideoCodecAV1:
78*d9f75844SAndroid Build Coastguard Worker return kVideoAv1;
79*d9f75844SAndroid Build Coastguard Worker default:
80*d9f75844SAndroid Build Coastguard Worker return kVideoUnknown;
81*d9f75844SAndroid Build Coastguard Worker }
82*d9f75844SAndroid Build Coastguard Worker }
83*d9f75844SAndroid Build Coastguard Worker
UpdateCodecTypeHistogram(const std::string & payload_name)84*d9f75844SAndroid Build Coastguard Worker void UpdateCodecTypeHistogram(const std::string& payload_name) {
85*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
86*d9f75844SAndroid Build Coastguard Worker PayloadNameToHistogramCodecType(payload_name),
87*d9f75844SAndroid Build Coastguard Worker kVideoMax);
88*d9f75844SAndroid Build Coastguard Worker }
89*d9f75844SAndroid Build Coastguard Worker
IsForcedFallbackPossible(const CodecSpecificInfo * codec_info,int simulcast_index)90*d9f75844SAndroid Build Coastguard Worker bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info,
91*d9f75844SAndroid Build Coastguard Worker int simulcast_index) {
92*d9f75844SAndroid Build Coastguard Worker return codec_info->codecType == kVideoCodecVP8 && simulcast_index == 0 &&
93*d9f75844SAndroid Build Coastguard Worker (codec_info->codecSpecific.VP8.temporalIdx == 0 ||
94*d9f75844SAndroid Build Coastguard Worker codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx);
95*d9f75844SAndroid Build Coastguard Worker }
96*d9f75844SAndroid Build Coastguard Worker
GetFallbackMaxPixels(const std::string & group)97*d9f75844SAndroid Build Coastguard Worker absl::optional<int> GetFallbackMaxPixels(const std::string& group) {
98*d9f75844SAndroid Build Coastguard Worker if (group.empty())
99*d9f75844SAndroid Build Coastguard Worker return absl::nullopt;
100*d9f75844SAndroid Build Coastguard Worker
101*d9f75844SAndroid Build Coastguard Worker int min_pixels;
102*d9f75844SAndroid Build Coastguard Worker int max_pixels;
103*d9f75844SAndroid Build Coastguard Worker int min_bps;
104*d9f75844SAndroid Build Coastguard Worker if (sscanf(group.c_str(), "-%d,%d,%d", &min_pixels, &max_pixels, &min_bps) !=
105*d9f75844SAndroid Build Coastguard Worker 3) {
106*d9f75844SAndroid Build Coastguard Worker return absl::optional<int>();
107*d9f75844SAndroid Build Coastguard Worker }
108*d9f75844SAndroid Build Coastguard Worker
109*d9f75844SAndroid Build Coastguard Worker if (min_pixels <= 0 || max_pixels <= 0 || max_pixels < min_pixels)
110*d9f75844SAndroid Build Coastguard Worker return absl::optional<int>();
111*d9f75844SAndroid Build Coastguard Worker
112*d9f75844SAndroid Build Coastguard Worker return absl::optional<int>(max_pixels);
113*d9f75844SAndroid Build Coastguard Worker }
114*d9f75844SAndroid Build Coastguard Worker
GetFallbackMaxPixelsIfFieldTrialEnabled(const webrtc::FieldTrialsView & field_trials)115*d9f75844SAndroid Build Coastguard Worker absl::optional<int> GetFallbackMaxPixelsIfFieldTrialEnabled(
116*d9f75844SAndroid Build Coastguard Worker const webrtc::FieldTrialsView& field_trials) {
117*d9f75844SAndroid Build Coastguard Worker std::string group = field_trials.Lookup(kVp8ForcedFallbackEncoderFieldTrial);
118*d9f75844SAndroid Build Coastguard Worker return (absl::StartsWith(group, "Enabled"))
119*d9f75844SAndroid Build Coastguard Worker ? GetFallbackMaxPixels(group.substr(7))
120*d9f75844SAndroid Build Coastguard Worker : absl::optional<int>();
121*d9f75844SAndroid Build Coastguard Worker }
122*d9f75844SAndroid Build Coastguard Worker
GetFallbackMaxPixelsIfFieldTrialDisabled(const webrtc::FieldTrialsView & field_trials)123*d9f75844SAndroid Build Coastguard Worker absl::optional<int> GetFallbackMaxPixelsIfFieldTrialDisabled(
124*d9f75844SAndroid Build Coastguard Worker const webrtc::FieldTrialsView& field_trials) {
125*d9f75844SAndroid Build Coastguard Worker std::string group = field_trials.Lookup(kVp8ForcedFallbackEncoderFieldTrial);
126*d9f75844SAndroid Build Coastguard Worker return (absl::StartsWith(group, "Disabled"))
127*d9f75844SAndroid Build Coastguard Worker ? GetFallbackMaxPixels(group.substr(8))
128*d9f75844SAndroid Build Coastguard Worker : absl::optional<int>();
129*d9f75844SAndroid Build Coastguard Worker }
130*d9f75844SAndroid Build Coastguard Worker } // namespace
131*d9f75844SAndroid Build Coastguard Worker
132*d9f75844SAndroid Build Coastguard Worker const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
133*d9f75844SAndroid Build Coastguard Worker
SendStatisticsProxy(Clock * clock,const VideoSendStream::Config & config,VideoEncoderConfig::ContentType content_type,const FieldTrialsView & field_trials)134*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::SendStatisticsProxy(
135*d9f75844SAndroid Build Coastguard Worker Clock* clock,
136*d9f75844SAndroid Build Coastguard Worker const VideoSendStream::Config& config,
137*d9f75844SAndroid Build Coastguard Worker VideoEncoderConfig::ContentType content_type,
138*d9f75844SAndroid Build Coastguard Worker const FieldTrialsView& field_trials)
139*d9f75844SAndroid Build Coastguard Worker : clock_(clock),
140*d9f75844SAndroid Build Coastguard Worker payload_name_(config.rtp.payload_name),
141*d9f75844SAndroid Build Coastguard Worker rtp_config_(config.rtp),
142*d9f75844SAndroid Build Coastguard Worker fallback_max_pixels_(
143*d9f75844SAndroid Build Coastguard Worker GetFallbackMaxPixelsIfFieldTrialEnabled(field_trials)),
144*d9f75844SAndroid Build Coastguard Worker fallback_max_pixels_disabled_(
145*d9f75844SAndroid Build Coastguard Worker GetFallbackMaxPixelsIfFieldTrialDisabled(field_trials)),
146*d9f75844SAndroid Build Coastguard Worker content_type_(content_type),
147*d9f75844SAndroid Build Coastguard Worker start_ms_(clock->TimeInMilliseconds()),
148*d9f75844SAndroid Build Coastguard Worker encode_time_(kEncodeTimeWeigthFactor),
149*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_(clock_),
150*d9f75844SAndroid Build Coastguard Worker media_byte_rate_tracker_(kBucketSizeMs, kBucketCount),
151*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_tracker_(kBucketSizeMs, kBucketCount),
152*d9f75844SAndroid Build Coastguard Worker last_num_spatial_layers_(0),
153*d9f75844SAndroid Build Coastguard Worker last_num_simulcast_streams_(0),
154*d9f75844SAndroid Build Coastguard Worker last_spatial_layer_use_{},
155*d9f75844SAndroid Build Coastguard Worker bw_limited_layers_(false),
156*d9f75844SAndroid Build Coastguard Worker internal_encoder_scaler_(false),
157*d9f75844SAndroid Build Coastguard Worker uma_container_(
158*d9f75844SAndroid Build Coastguard Worker new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
159*d9f75844SAndroid Build Coastguard Worker }
160*d9f75844SAndroid Build Coastguard Worker
~SendStatisticsProxy()161*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::~SendStatisticsProxy() {
162*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
163*d9f75844SAndroid Build Coastguard Worker uma_container_->UpdateHistograms(rtp_config_, stats_);
164*d9f75844SAndroid Build Coastguard Worker
165*d9f75844SAndroid Build Coastguard Worker int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
166*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
167*d9f75844SAndroid Build Coastguard Worker elapsed_sec);
168*d9f75844SAndroid Build Coastguard Worker
169*d9f75844SAndroid Build Coastguard Worker if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
170*d9f75844SAndroid Build Coastguard Worker UpdateCodecTypeHistogram(payload_name_);
171*d9f75844SAndroid Build Coastguard Worker }
172*d9f75844SAndroid Build Coastguard Worker
173*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::FallbackEncoderInfo::FallbackEncoderInfo() = default;
174*d9f75844SAndroid Build Coastguard Worker
UmaSamplesContainer(const char * prefix,const VideoSendStream::Stats & stats,Clock * const clock)175*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
176*d9f75844SAndroid Build Coastguard Worker const char* prefix,
177*d9f75844SAndroid Build Coastguard Worker const VideoSendStream::Stats& stats,
178*d9f75844SAndroid Build Coastguard Worker Clock* const clock)
179*d9f75844SAndroid Build Coastguard Worker : uma_prefix_(prefix),
180*d9f75844SAndroid Build Coastguard Worker clock_(clock),
181*d9f75844SAndroid Build Coastguard Worker input_frame_rate_tracker_(100, 10u),
182*d9f75844SAndroid Build Coastguard Worker input_fps_counter_(clock, nullptr, true),
183*d9f75844SAndroid Build Coastguard Worker sent_fps_counter_(clock, nullptr, true),
184*d9f75844SAndroid Build Coastguard Worker total_byte_counter_(clock, nullptr, true),
185*d9f75844SAndroid Build Coastguard Worker media_byte_counter_(clock, nullptr, true),
186*d9f75844SAndroid Build Coastguard Worker rtx_byte_counter_(clock, nullptr, true),
187*d9f75844SAndroid Build Coastguard Worker padding_byte_counter_(clock, nullptr, true),
188*d9f75844SAndroid Build Coastguard Worker retransmit_byte_counter_(clock, nullptr, true),
189*d9f75844SAndroid Build Coastguard Worker fec_byte_counter_(clock, nullptr, true),
190*d9f75844SAndroid Build Coastguard Worker first_rtcp_stats_time_ms_(-1),
191*d9f75844SAndroid Build Coastguard Worker first_rtp_stats_time_ms_(-1),
192*d9f75844SAndroid Build Coastguard Worker start_stats_(stats),
193*d9f75844SAndroid Build Coastguard Worker num_streams_(0),
194*d9f75844SAndroid Build Coastguard Worker num_pixels_highest_stream_(0) {
195*d9f75844SAndroid Build Coastguard Worker InitializeBitrateCounters(stats);
196*d9f75844SAndroid Build Coastguard Worker static_assert(
197*d9f75844SAndroid Build Coastguard Worker kMaxEncodedFrameTimestampDiff < std::numeric_limits<uint32_t>::max() / 2,
198*d9f75844SAndroid Build Coastguard Worker "has to be smaller than half range");
199*d9f75844SAndroid Build Coastguard Worker }
200*d9f75844SAndroid Build Coastguard Worker
~UmaSamplesContainer()201*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
202*d9f75844SAndroid Build Coastguard Worker
InitializeBitrateCounters(const VideoSendStream::Stats & stats)203*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UmaSamplesContainer::InitializeBitrateCounters(
204*d9f75844SAndroid Build Coastguard Worker const VideoSendStream::Stats& stats) {
205*d9f75844SAndroid Build Coastguard Worker for (const auto& it : stats.substreams) {
206*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc = it.first;
207*d9f75844SAndroid Build Coastguard Worker total_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
208*d9f75844SAndroid Build Coastguard Worker ssrc);
209*d9f75844SAndroid Build Coastguard Worker padding_byte_counter_.SetLast(it.second.rtp_stats.transmitted.padding_bytes,
210*d9f75844SAndroid Build Coastguard Worker ssrc);
211*d9f75844SAndroid Build Coastguard Worker retransmit_byte_counter_.SetLast(
212*d9f75844SAndroid Build Coastguard Worker it.second.rtp_stats.retransmitted.TotalBytes(), ssrc);
213*d9f75844SAndroid Build Coastguard Worker fec_byte_counter_.SetLast(it.second.rtp_stats.fec.TotalBytes(), ssrc);
214*d9f75844SAndroid Build Coastguard Worker switch (it.second.type) {
215*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kMedia:
216*d9f75844SAndroid Build Coastguard Worker media_byte_counter_.SetLast(it.second.rtp_stats.MediaPayloadBytes(),
217*d9f75844SAndroid Build Coastguard Worker ssrc);
218*d9f75844SAndroid Build Coastguard Worker break;
219*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kRtx:
220*d9f75844SAndroid Build Coastguard Worker rtx_byte_counter_.SetLast(it.second.rtp_stats.transmitted.TotalBytes(),
221*d9f75844SAndroid Build Coastguard Worker ssrc);
222*d9f75844SAndroid Build Coastguard Worker break;
223*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kFlexfec:
224*d9f75844SAndroid Build Coastguard Worker break;
225*d9f75844SAndroid Build Coastguard Worker }
226*d9f75844SAndroid Build Coastguard Worker }
227*d9f75844SAndroid Build Coastguard Worker }
228*d9f75844SAndroid Build Coastguard Worker
RemoveOld(int64_t now_ms)229*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UmaSamplesContainer::RemoveOld(int64_t now_ms) {
230*d9f75844SAndroid Build Coastguard Worker while (!encoded_frames_.empty()) {
231*d9f75844SAndroid Build Coastguard Worker auto it = encoded_frames_.begin();
232*d9f75844SAndroid Build Coastguard Worker if (now_ms - it->second.send_ms < kMaxEncodedFrameWindowMs)
233*d9f75844SAndroid Build Coastguard Worker break;
234*d9f75844SAndroid Build Coastguard Worker
235*d9f75844SAndroid Build Coastguard Worker // Use max per timestamp.
236*d9f75844SAndroid Build Coastguard Worker sent_width_counter_.Add(it->second.max_width);
237*d9f75844SAndroid Build Coastguard Worker sent_height_counter_.Add(it->second.max_height);
238*d9f75844SAndroid Build Coastguard Worker
239*d9f75844SAndroid Build Coastguard Worker // Check number of encoded streams per timestamp.
240*d9f75844SAndroid Build Coastguard Worker if (num_streams_ > static_cast<size_t>(it->second.max_simulcast_idx)) {
241*d9f75844SAndroid Build Coastguard Worker if (num_streams_ > 1) {
242*d9f75844SAndroid Build Coastguard Worker int disabled_streams =
243*d9f75844SAndroid Build Coastguard Worker static_cast<int>(num_streams_ - 1 - it->second.max_simulcast_idx);
244*d9f75844SAndroid Build Coastguard Worker // Can be limited in resolution or framerate.
245*d9f75844SAndroid Build Coastguard Worker uint32_t pixels = it->second.max_width * it->second.max_height;
246*d9f75844SAndroid Build Coastguard Worker bool bw_limited_resolution =
247*d9f75844SAndroid Build Coastguard Worker disabled_streams > 0 && pixels < num_pixels_highest_stream_;
248*d9f75844SAndroid Build Coastguard Worker bw_limited_frame_counter_.Add(bw_limited_resolution);
249*d9f75844SAndroid Build Coastguard Worker if (bw_limited_resolution) {
250*d9f75844SAndroid Build Coastguard Worker bw_resolutions_disabled_counter_.Add(disabled_streams);
251*d9f75844SAndroid Build Coastguard Worker }
252*d9f75844SAndroid Build Coastguard Worker }
253*d9f75844SAndroid Build Coastguard Worker }
254*d9f75844SAndroid Build Coastguard Worker encoded_frames_.erase(it);
255*d9f75844SAndroid Build Coastguard Worker }
256*d9f75844SAndroid Build Coastguard Worker }
257*d9f75844SAndroid Build Coastguard Worker
InsertEncodedFrame(const EncodedImage & encoded_frame,int simulcast_idx)258*d9f75844SAndroid Build Coastguard Worker bool SendStatisticsProxy::UmaSamplesContainer::InsertEncodedFrame(
259*d9f75844SAndroid Build Coastguard Worker const EncodedImage& encoded_frame,
260*d9f75844SAndroid Build Coastguard Worker int simulcast_idx) {
261*d9f75844SAndroid Build Coastguard Worker int64_t now_ms = clock_->TimeInMilliseconds();
262*d9f75844SAndroid Build Coastguard Worker RemoveOld(now_ms);
263*d9f75844SAndroid Build Coastguard Worker if (encoded_frames_.size() > kMaxEncodedFrameMapSize) {
264*d9f75844SAndroid Build Coastguard Worker encoded_frames_.clear();
265*d9f75844SAndroid Build Coastguard Worker }
266*d9f75844SAndroid Build Coastguard Worker
267*d9f75844SAndroid Build Coastguard Worker // Check for jump in timestamp.
268*d9f75844SAndroid Build Coastguard Worker if (!encoded_frames_.empty()) {
269*d9f75844SAndroid Build Coastguard Worker uint32_t oldest_timestamp = encoded_frames_.begin()->first;
270*d9f75844SAndroid Build Coastguard Worker if (ForwardDiff(oldest_timestamp, encoded_frame.Timestamp()) >
271*d9f75844SAndroid Build Coastguard Worker kMaxEncodedFrameTimestampDiff) {
272*d9f75844SAndroid Build Coastguard Worker // Gap detected, clear frames to have a sequence where newest timestamp
273*d9f75844SAndroid Build Coastguard Worker // is not too far away from oldest in order to distinguish old and new.
274*d9f75844SAndroid Build Coastguard Worker encoded_frames_.clear();
275*d9f75844SAndroid Build Coastguard Worker }
276*d9f75844SAndroid Build Coastguard Worker }
277*d9f75844SAndroid Build Coastguard Worker
278*d9f75844SAndroid Build Coastguard Worker auto it = encoded_frames_.find(encoded_frame.Timestamp());
279*d9f75844SAndroid Build Coastguard Worker if (it == encoded_frames_.end()) {
280*d9f75844SAndroid Build Coastguard Worker // First frame with this timestamp.
281*d9f75844SAndroid Build Coastguard Worker encoded_frames_.insert(
282*d9f75844SAndroid Build Coastguard Worker std::make_pair(encoded_frame.Timestamp(),
283*d9f75844SAndroid Build Coastguard Worker Frame(now_ms, encoded_frame._encodedWidth,
284*d9f75844SAndroid Build Coastguard Worker encoded_frame._encodedHeight, simulcast_idx)));
285*d9f75844SAndroid Build Coastguard Worker sent_fps_counter_.Add(1);
286*d9f75844SAndroid Build Coastguard Worker return true;
287*d9f75844SAndroid Build Coastguard Worker }
288*d9f75844SAndroid Build Coastguard Worker
289*d9f75844SAndroid Build Coastguard Worker it->second.max_width =
290*d9f75844SAndroid Build Coastguard Worker std::max(it->second.max_width, encoded_frame._encodedWidth);
291*d9f75844SAndroid Build Coastguard Worker it->second.max_height =
292*d9f75844SAndroid Build Coastguard Worker std::max(it->second.max_height, encoded_frame._encodedHeight);
293*d9f75844SAndroid Build Coastguard Worker it->second.max_simulcast_idx =
294*d9f75844SAndroid Build Coastguard Worker std::max(it->second.max_simulcast_idx, simulcast_idx);
295*d9f75844SAndroid Build Coastguard Worker return false;
296*d9f75844SAndroid Build Coastguard Worker }
297*d9f75844SAndroid Build Coastguard Worker
UpdateHistograms(const RtpConfig & rtp_config,const VideoSendStream::Stats & current_stats)298*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
299*d9f75844SAndroid Build Coastguard Worker const RtpConfig& rtp_config,
300*d9f75844SAndroid Build Coastguard Worker const VideoSendStream::Stats& current_stats) {
301*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
302*d9f75844SAndroid Build Coastguard Worker const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
303*d9f75844SAndroid Build Coastguard Worker const int kMinRequiredPeriodicSamples = 6;
304*d9f75844SAndroid Build Coastguard Worker char log_stream_buf[8 * 1024];
305*d9f75844SAndroid Build Coastguard Worker rtc::SimpleStringBuilder log_stream(log_stream_buf);
306*d9f75844SAndroid Build Coastguard Worker int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
307*d9f75844SAndroid Build Coastguard Worker int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
308*d9f75844SAndroid Build Coastguard Worker if (in_width != -1) {
309*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
310*d9f75844SAndroid Build Coastguard Worker in_width);
311*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
312*d9f75844SAndroid Build Coastguard Worker in_height);
313*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "InputWidthInPixels " << in_width << "\n"
314*d9f75844SAndroid Build Coastguard Worker << uma_prefix_ << "InputHeightInPixels " << in_height << "\n";
315*d9f75844SAndroid Build Coastguard Worker }
316*d9f75844SAndroid Build Coastguard Worker AggregatedStats in_fps = input_fps_counter_.GetStats();
317*d9f75844SAndroid Build Coastguard Worker if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
318*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
319*d9f75844SAndroid Build Coastguard Worker in_fps.average);
320*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "InputFramesPerSecond " << in_fps.ToString()
321*d9f75844SAndroid Build Coastguard Worker << "\n";
322*d9f75844SAndroid Build Coastguard Worker }
323*d9f75844SAndroid Build Coastguard Worker
324*d9f75844SAndroid Build Coastguard Worker int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
325*d9f75844SAndroid Build Coastguard Worker int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
326*d9f75844SAndroid Build Coastguard Worker if (sent_width != -1) {
327*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
328*d9f75844SAndroid Build Coastguard Worker sent_width);
329*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
330*d9f75844SAndroid Build Coastguard Worker sent_height);
331*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "SentWidthInPixels " << sent_width << "\n"
332*d9f75844SAndroid Build Coastguard Worker << uma_prefix_ << "SentHeightInPixels " << sent_height << "\n";
333*d9f75844SAndroid Build Coastguard Worker }
334*d9f75844SAndroid Build Coastguard Worker AggregatedStats sent_fps = sent_fps_counter_.GetStats();
335*d9f75844SAndroid Build Coastguard Worker if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
336*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
337*d9f75844SAndroid Build Coastguard Worker sent_fps.average);
338*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "SentFramesPerSecond " << sent_fps.ToString()
339*d9f75844SAndroid Build Coastguard Worker << "\n";
340*d9f75844SAndroid Build Coastguard Worker }
341*d9f75844SAndroid Build Coastguard Worker
342*d9f75844SAndroid Build Coastguard Worker if (in_fps.num_samples > kMinRequiredPeriodicSamples &&
343*d9f75844SAndroid Build Coastguard Worker sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
344*d9f75844SAndroid Build Coastguard Worker int in_fps_avg = in_fps.average;
345*d9f75844SAndroid Build Coastguard Worker if (in_fps_avg > 0) {
346*d9f75844SAndroid Build Coastguard Worker int sent_fps_avg = sent_fps.average;
347*d9f75844SAndroid Build Coastguard Worker int sent_to_in_fps_ratio_percent =
348*d9f75844SAndroid Build Coastguard Worker (100 * sent_fps_avg + in_fps_avg / 2) / in_fps_avg;
349*d9f75844SAndroid Build Coastguard Worker // If reported period is small, it may happen that sent_fps is larger than
350*d9f75844SAndroid Build Coastguard Worker // input_fps briefly on average. This should be treated as 100% sent to
351*d9f75844SAndroid Build Coastguard Worker // input ratio.
352*d9f75844SAndroid Build Coastguard Worker if (sent_to_in_fps_ratio_percent > 100)
353*d9f75844SAndroid Build Coastguard Worker sent_to_in_fps_ratio_percent = 100;
354*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(kIndex,
355*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "SentToInputFpsRatioPercent",
356*d9f75844SAndroid Build Coastguard Worker sent_to_in_fps_ratio_percent);
357*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "SentToInputFpsRatioPercent "
358*d9f75844SAndroid Build Coastguard Worker << sent_to_in_fps_ratio_percent << "\n";
359*d9f75844SAndroid Build Coastguard Worker }
360*d9f75844SAndroid Build Coastguard Worker }
361*d9f75844SAndroid Build Coastguard Worker
362*d9f75844SAndroid Build Coastguard Worker int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
363*d9f75844SAndroid Build Coastguard Worker if (encode_ms != -1) {
364*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
365*d9f75844SAndroid Build Coastguard Worker encode_ms);
366*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "EncodeTimeInMs " << encode_ms << "\n";
367*d9f75844SAndroid Build Coastguard Worker }
368*d9f75844SAndroid Build Coastguard Worker int key_frames_permille =
369*d9f75844SAndroid Build Coastguard Worker key_frame_counter_.Permille(kMinRequiredMetricsSamples);
370*d9f75844SAndroid Build Coastguard Worker if (key_frames_permille != -1) {
371*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
372*d9f75844SAndroid Build Coastguard Worker key_frames_permille);
373*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "KeyFramesSentInPermille "
374*d9f75844SAndroid Build Coastguard Worker << key_frames_permille << "\n";
375*d9f75844SAndroid Build Coastguard Worker }
376*d9f75844SAndroid Build Coastguard Worker int quality_limited =
377*d9f75844SAndroid Build Coastguard Worker quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
378*d9f75844SAndroid Build Coastguard Worker if (quality_limited != -1) {
379*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(kIndex,
380*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "QualityLimitedResolutionInPercent",
381*d9f75844SAndroid Build Coastguard Worker quality_limited);
382*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "QualityLimitedResolutionInPercent "
383*d9f75844SAndroid Build Coastguard Worker << quality_limited << "\n";
384*d9f75844SAndroid Build Coastguard Worker }
385*d9f75844SAndroid Build Coastguard Worker int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
386*d9f75844SAndroid Build Coastguard Worker if (downscales != -1) {
387*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_ENUMERATION(
388*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
389*d9f75844SAndroid Build Coastguard Worker 20);
390*d9f75844SAndroid Build Coastguard Worker }
391*d9f75844SAndroid Build Coastguard Worker int cpu_limited =
392*d9f75844SAndroid Build Coastguard Worker cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
393*d9f75844SAndroid Build Coastguard Worker if (cpu_limited != -1) {
394*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(
395*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
396*d9f75844SAndroid Build Coastguard Worker }
397*d9f75844SAndroid Build Coastguard Worker int bw_limited =
398*d9f75844SAndroid Build Coastguard Worker bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
399*d9f75844SAndroid Build Coastguard Worker if (bw_limited != -1) {
400*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(
401*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
402*d9f75844SAndroid Build Coastguard Worker bw_limited);
403*d9f75844SAndroid Build Coastguard Worker }
404*d9f75844SAndroid Build Coastguard Worker int num_disabled =
405*d9f75844SAndroid Build Coastguard Worker bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
406*d9f75844SAndroid Build Coastguard Worker if (num_disabled != -1) {
407*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_ENUMERATION(
408*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
409*d9f75844SAndroid Build Coastguard Worker num_disabled, 10);
410*d9f75844SAndroid Build Coastguard Worker }
411*d9f75844SAndroid Build Coastguard Worker int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
412*d9f75844SAndroid Build Coastguard Worker if (delay_ms != -1)
413*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
414*d9f75844SAndroid Build Coastguard Worker delay_ms);
415*d9f75844SAndroid Build Coastguard Worker
416*d9f75844SAndroid Build Coastguard Worker int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
417*d9f75844SAndroid Build Coastguard Worker if (max_delay_ms != -1) {
418*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
419*d9f75844SAndroid Build Coastguard Worker max_delay_ms);
420*d9f75844SAndroid Build Coastguard Worker }
421*d9f75844SAndroid Build Coastguard Worker
422*d9f75844SAndroid Build Coastguard Worker for (const auto& it : qp_counters_) {
423*d9f75844SAndroid Build Coastguard Worker int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
424*d9f75844SAndroid Build Coastguard Worker if (qp_vp8 != -1) {
425*d9f75844SAndroid Build Coastguard Worker int spatial_idx = it.first;
426*d9f75844SAndroid Build Coastguard Worker if (spatial_idx == -1) {
427*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
428*d9f75844SAndroid Build Coastguard Worker qp_vp8);
429*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 0) {
430*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
431*d9f75844SAndroid Build Coastguard Worker qp_vp8);
432*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 1) {
433*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
434*d9f75844SAndroid Build Coastguard Worker qp_vp8);
435*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 2) {
436*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
437*d9f75844SAndroid Build Coastguard Worker qp_vp8);
438*d9f75844SAndroid Build Coastguard Worker } else {
439*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_WARNING)
440*d9f75844SAndroid Build Coastguard Worker << "QP stats not recorded for VP8 spatial idx " << spatial_idx;
441*d9f75844SAndroid Build Coastguard Worker }
442*d9f75844SAndroid Build Coastguard Worker }
443*d9f75844SAndroid Build Coastguard Worker int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
444*d9f75844SAndroid Build Coastguard Worker if (qp_vp9 != -1) {
445*d9f75844SAndroid Build Coastguard Worker int spatial_idx = it.first;
446*d9f75844SAndroid Build Coastguard Worker if (spatial_idx == -1) {
447*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
448*d9f75844SAndroid Build Coastguard Worker qp_vp9);
449*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 0) {
450*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
451*d9f75844SAndroid Build Coastguard Worker qp_vp9);
452*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 1) {
453*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
454*d9f75844SAndroid Build Coastguard Worker qp_vp9);
455*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 2) {
456*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
457*d9f75844SAndroid Build Coastguard Worker qp_vp9);
458*d9f75844SAndroid Build Coastguard Worker } else {
459*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_WARNING)
460*d9f75844SAndroid Build Coastguard Worker << "QP stats not recorded for VP9 spatial layer " << spatial_idx;
461*d9f75844SAndroid Build Coastguard Worker }
462*d9f75844SAndroid Build Coastguard Worker }
463*d9f75844SAndroid Build Coastguard Worker int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
464*d9f75844SAndroid Build Coastguard Worker if (qp_h264 != -1) {
465*d9f75844SAndroid Build Coastguard Worker int spatial_idx = it.first;
466*d9f75844SAndroid Build Coastguard Worker if (spatial_idx == -1) {
467*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264",
468*d9f75844SAndroid Build Coastguard Worker qp_h264);
469*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 0) {
470*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S0",
471*d9f75844SAndroid Build Coastguard Worker qp_h264);
472*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 1) {
473*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S1",
474*d9f75844SAndroid Build Coastguard Worker qp_h264);
475*d9f75844SAndroid Build Coastguard Worker } else if (spatial_idx == 2) {
476*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.H264.S2",
477*d9f75844SAndroid Build Coastguard Worker qp_h264);
478*d9f75844SAndroid Build Coastguard Worker } else {
479*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_WARNING)
480*d9f75844SAndroid Build Coastguard Worker << "QP stats not recorded for H264 spatial idx " << spatial_idx;
481*d9f75844SAndroid Build Coastguard Worker }
482*d9f75844SAndroid Build Coastguard Worker }
483*d9f75844SAndroid Build Coastguard Worker }
484*d9f75844SAndroid Build Coastguard Worker
485*d9f75844SAndroid Build Coastguard Worker if (first_rtp_stats_time_ms_ != -1) {
486*d9f75844SAndroid Build Coastguard Worker quality_adapt_timer_.Stop(clock_->TimeInMilliseconds());
487*d9f75844SAndroid Build Coastguard Worker int64_t elapsed_sec = quality_adapt_timer_.total_ms / 1000;
488*d9f75844SAndroid Build Coastguard Worker if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
489*d9f75844SAndroid Build Coastguard Worker int quality_changes = current_stats.number_of_quality_adapt_changes -
490*d9f75844SAndroid Build Coastguard Worker start_stats_.number_of_quality_adapt_changes;
491*d9f75844SAndroid Build Coastguard Worker // Only base stats on changes during a call, discard initial changes.
492*d9f75844SAndroid Build Coastguard Worker int initial_changes =
493*d9f75844SAndroid Build Coastguard Worker initial_quality_changes_.down + initial_quality_changes_.up;
494*d9f75844SAndroid Build Coastguard Worker if (initial_changes <= quality_changes)
495*d9f75844SAndroid Build Coastguard Worker quality_changes -= initial_changes;
496*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(kIndex,
497*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "AdaptChangesPerMinute.Quality",
498*d9f75844SAndroid Build Coastguard Worker quality_changes * 60 / elapsed_sec);
499*d9f75844SAndroid Build Coastguard Worker }
500*d9f75844SAndroid Build Coastguard Worker cpu_adapt_timer_.Stop(clock_->TimeInMilliseconds());
501*d9f75844SAndroid Build Coastguard Worker elapsed_sec = cpu_adapt_timer_.total_ms / 1000;
502*d9f75844SAndroid Build Coastguard Worker if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
503*d9f75844SAndroid Build Coastguard Worker int cpu_changes = current_stats.number_of_cpu_adapt_changes -
504*d9f75844SAndroid Build Coastguard Worker start_stats_.number_of_cpu_adapt_changes;
505*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(kIndex,
506*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "AdaptChangesPerMinute.Cpu",
507*d9f75844SAndroid Build Coastguard Worker cpu_changes * 60 / elapsed_sec);
508*d9f75844SAndroid Build Coastguard Worker }
509*d9f75844SAndroid Build Coastguard Worker }
510*d9f75844SAndroid Build Coastguard Worker
511*d9f75844SAndroid Build Coastguard Worker if (first_rtcp_stats_time_ms_ != -1) {
512*d9f75844SAndroid Build Coastguard Worker int64_t elapsed_sec =
513*d9f75844SAndroid Build Coastguard Worker (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
514*d9f75844SAndroid Build Coastguard Worker if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
515*d9f75844SAndroid Build Coastguard Worker int fraction_lost = report_block_stats_.FractionLostInPercent();
516*d9f75844SAndroid Build Coastguard Worker if (fraction_lost != -1) {
517*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(
518*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
519*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "SentPacketsLostInPercent "
520*d9f75844SAndroid Build Coastguard Worker << fraction_lost << "\n";
521*d9f75844SAndroid Build Coastguard Worker }
522*d9f75844SAndroid Build Coastguard Worker
523*d9f75844SAndroid Build Coastguard Worker // The RTCP packet type counters, delivered via the
524*d9f75844SAndroid Build Coastguard Worker // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
525*d9f75844SAndroid Build Coastguard Worker // life of the send stream and are not reset when switching content type.
526*d9f75844SAndroid Build Coastguard Worker // For the purpose of these statistics though, we want new counts when
527*d9f75844SAndroid Build Coastguard Worker // switching since we switch histogram name. On every reset of the
528*d9f75844SAndroid Build Coastguard Worker // UmaSamplesContainer, we save the initial state of the counters, so that
529*d9f75844SAndroid Build Coastguard Worker // we can calculate the delta here and aggregate over all ssrcs.
530*d9f75844SAndroid Build Coastguard Worker RtcpPacketTypeCounter counters;
531*d9f75844SAndroid Build Coastguard Worker for (uint32_t ssrc : rtp_config.ssrcs) {
532*d9f75844SAndroid Build Coastguard Worker auto kv = current_stats.substreams.find(ssrc);
533*d9f75844SAndroid Build Coastguard Worker if (kv == current_stats.substreams.end())
534*d9f75844SAndroid Build Coastguard Worker continue;
535*d9f75844SAndroid Build Coastguard Worker
536*d9f75844SAndroid Build Coastguard Worker RtcpPacketTypeCounter stream_counters =
537*d9f75844SAndroid Build Coastguard Worker kv->second.rtcp_packet_type_counts;
538*d9f75844SAndroid Build Coastguard Worker kv = start_stats_.substreams.find(ssrc);
539*d9f75844SAndroid Build Coastguard Worker if (kv != start_stats_.substreams.end())
540*d9f75844SAndroid Build Coastguard Worker stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
541*d9f75844SAndroid Build Coastguard Worker
542*d9f75844SAndroid Build Coastguard Worker counters.Add(stream_counters);
543*d9f75844SAndroid Build Coastguard Worker }
544*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex,
545*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "NackPacketsReceivedPerMinute",
546*d9f75844SAndroid Build Coastguard Worker counters.nack_packets * 60 / elapsed_sec);
547*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex,
548*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "FirPacketsReceivedPerMinute",
549*d9f75844SAndroid Build Coastguard Worker counters.fir_packets * 60 / elapsed_sec);
550*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex,
551*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "PliPacketsReceivedPerMinute",
552*d9f75844SAndroid Build Coastguard Worker counters.pli_packets * 60 / elapsed_sec);
553*d9f75844SAndroid Build Coastguard Worker if (counters.nack_requests > 0) {
554*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(
555*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
556*d9f75844SAndroid Build Coastguard Worker counters.UniqueNackRequestsInPercent());
557*d9f75844SAndroid Build Coastguard Worker }
558*d9f75844SAndroid Build Coastguard Worker }
559*d9f75844SAndroid Build Coastguard Worker }
560*d9f75844SAndroid Build Coastguard Worker
561*d9f75844SAndroid Build Coastguard Worker if (first_rtp_stats_time_ms_ != -1) {
562*d9f75844SAndroid Build Coastguard Worker int64_t elapsed_sec =
563*d9f75844SAndroid Build Coastguard Worker (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
564*d9f75844SAndroid Build Coastguard Worker if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
565*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
566*d9f75844SAndroid Build Coastguard Worker target_rate_updates_.pause_resume_events);
567*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "NumberOfPauseEvents "
568*d9f75844SAndroid Build Coastguard Worker << target_rate_updates_.pause_resume_events << "\n";
569*d9f75844SAndroid Build Coastguard Worker
570*d9f75844SAndroid Build Coastguard Worker int paused_time_percent =
571*d9f75844SAndroid Build Coastguard Worker paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
572*d9f75844SAndroid Build Coastguard Worker if (paused_time_percent != -1) {
573*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
574*d9f75844SAndroid Build Coastguard Worker paused_time_percent);
575*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "PausedTimeInPercent "
576*d9f75844SAndroid Build Coastguard Worker << paused_time_percent << "\n";
577*d9f75844SAndroid Build Coastguard Worker }
578*d9f75844SAndroid Build Coastguard Worker }
579*d9f75844SAndroid Build Coastguard Worker }
580*d9f75844SAndroid Build Coastguard Worker
581*d9f75844SAndroid Build Coastguard Worker if (fallback_info_.is_possible) {
582*d9f75844SAndroid Build Coastguard Worker // Double interval since there is some time before fallback may occur.
583*d9f75844SAndroid Build Coastguard Worker const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000;
584*d9f75844SAndroid Build Coastguard Worker int64_t elapsed_ms = fallback_info_.elapsed_ms;
585*d9f75844SAndroid Build Coastguard Worker int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs);
586*d9f75844SAndroid Build Coastguard Worker if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) {
587*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_PERCENTAGE(
588*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8",
589*d9f75844SAndroid Build Coastguard Worker fallback_time_percent);
590*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_100(
591*d9f75844SAndroid Build Coastguard Worker kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8",
592*d9f75844SAndroid Build Coastguard Worker fallback_info_.on_off_events * 60 / (elapsed_ms / 1000));
593*d9f75844SAndroid Build Coastguard Worker }
594*d9f75844SAndroid Build Coastguard Worker }
595*d9f75844SAndroid Build Coastguard Worker
596*d9f75844SAndroid Build Coastguard Worker AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats();
597*d9f75844SAndroid Build Coastguard Worker if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
598*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps",
599*d9f75844SAndroid Build Coastguard Worker total_bytes_per_sec.average * 8 / 1000);
600*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "BitrateSentInBps "
601*d9f75844SAndroid Build Coastguard Worker << total_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
602*d9f75844SAndroid Build Coastguard Worker }
603*d9f75844SAndroid Build Coastguard Worker AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats();
604*d9f75844SAndroid Build Coastguard Worker if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
605*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
606*d9f75844SAndroid Build Coastguard Worker media_bytes_per_sec.average * 8 / 1000);
607*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "MediaBitrateSentInBps "
608*d9f75844SAndroid Build Coastguard Worker << media_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
609*d9f75844SAndroid Build Coastguard Worker }
610*d9f75844SAndroid Build Coastguard Worker AggregatedStats padding_bytes_per_sec = padding_byte_counter_.GetStats();
611*d9f75844SAndroid Build Coastguard Worker if (padding_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
612*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex,
613*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "PaddingBitrateSentInKbps",
614*d9f75844SAndroid Build Coastguard Worker padding_bytes_per_sec.average * 8 / 1000);
615*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "PaddingBitrateSentInBps "
616*d9f75844SAndroid Build Coastguard Worker << padding_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
617*d9f75844SAndroid Build Coastguard Worker }
618*d9f75844SAndroid Build Coastguard Worker AggregatedStats retransmit_bytes_per_sec =
619*d9f75844SAndroid Build Coastguard Worker retransmit_byte_counter_.GetStats();
620*d9f75844SAndroid Build Coastguard Worker if (retransmit_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
621*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex,
622*d9f75844SAndroid Build Coastguard Worker uma_prefix_ + "RetransmittedBitrateSentInKbps",
623*d9f75844SAndroid Build Coastguard Worker retransmit_bytes_per_sec.average * 8 / 1000);
624*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "RetransmittedBitrateSentInBps "
625*d9f75844SAndroid Build Coastguard Worker << retransmit_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
626*d9f75844SAndroid Build Coastguard Worker }
627*d9f75844SAndroid Build Coastguard Worker if (!rtp_config.rtx.ssrcs.empty()) {
628*d9f75844SAndroid Build Coastguard Worker AggregatedStats rtx_bytes_per_sec = rtx_byte_counter_.GetStats();
629*d9f75844SAndroid Build Coastguard Worker int rtx_bytes_per_sec_avg = -1;
630*d9f75844SAndroid Build Coastguard Worker if (rtx_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
631*d9f75844SAndroid Build Coastguard Worker rtx_bytes_per_sec_avg = rtx_bytes_per_sec.average;
632*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "RtxBitrateSentInBps "
633*d9f75844SAndroid Build Coastguard Worker << rtx_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
634*d9f75844SAndroid Build Coastguard Worker } else if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
635*d9f75844SAndroid Build Coastguard Worker rtx_bytes_per_sec_avg = 0; // RTX enabled but no RTX data sent, record 0.
636*d9f75844SAndroid Build Coastguard Worker }
637*d9f75844SAndroid Build Coastguard Worker if (rtx_bytes_per_sec_avg != -1) {
638*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
639*d9f75844SAndroid Build Coastguard Worker rtx_bytes_per_sec_avg * 8 / 1000);
640*d9f75844SAndroid Build Coastguard Worker }
641*d9f75844SAndroid Build Coastguard Worker }
642*d9f75844SAndroid Build Coastguard Worker if (rtp_config.flexfec.payload_type != -1 ||
643*d9f75844SAndroid Build Coastguard Worker rtp_config.ulpfec.red_payload_type != -1) {
644*d9f75844SAndroid Build Coastguard Worker AggregatedStats fec_bytes_per_sec = fec_byte_counter_.GetStats();
645*d9f75844SAndroid Build Coastguard Worker if (fec_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) {
646*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "FecBitrateSentInKbps",
647*d9f75844SAndroid Build Coastguard Worker fec_bytes_per_sec.average * 8 / 1000);
648*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "FecBitrateSentInBps "
649*d9f75844SAndroid Build Coastguard Worker << fec_bytes_per_sec.ToStringWithMultiplier(8) << "\n";
650*d9f75844SAndroid Build Coastguard Worker }
651*d9f75844SAndroid Build Coastguard Worker }
652*d9f75844SAndroid Build Coastguard Worker log_stream << "Frames encoded " << current_stats.frames_encoded << "\n"
653*d9f75844SAndroid Build Coastguard Worker << uma_prefix_ << "DroppedFrames.Capturer "
654*d9f75844SAndroid Build Coastguard Worker << current_stats.frames_dropped_by_capturer << "\n";
655*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Capturer",
656*d9f75844SAndroid Build Coastguard Worker current_stats.frames_dropped_by_capturer);
657*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "DroppedFrames.EncoderQueue "
658*d9f75844SAndroid Build Coastguard Worker << current_stats.frames_dropped_by_encoder_queue << "\n";
659*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.EncoderQueue",
660*d9f75844SAndroid Build Coastguard Worker current_stats.frames_dropped_by_encoder_queue);
661*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "DroppedFrames.Encoder "
662*d9f75844SAndroid Build Coastguard Worker << current_stats.frames_dropped_by_encoder << "\n";
663*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Encoder",
664*d9f75844SAndroid Build Coastguard Worker current_stats.frames_dropped_by_encoder);
665*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "DroppedFrames.Ratelimiter "
666*d9f75844SAndroid Build Coastguard Worker << current_stats.frames_dropped_by_rate_limiter << "\n";
667*d9f75844SAndroid Build Coastguard Worker RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "DroppedFrames.Ratelimiter",
668*d9f75844SAndroid Build Coastguard Worker current_stats.frames_dropped_by_rate_limiter);
669*d9f75844SAndroid Build Coastguard Worker log_stream << uma_prefix_ << "DroppedFrames.CongestionWindow "
670*d9f75844SAndroid Build Coastguard Worker << current_stats.frames_dropped_by_congestion_window;
671*d9f75844SAndroid Build Coastguard Worker
672*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_INFO) << log_stream.str();
673*d9f75844SAndroid Build Coastguard Worker }
674*d9f75844SAndroid Build Coastguard Worker
OnEncoderReconfigured(const VideoEncoderConfig & config,const std::vector<VideoStream> & streams)675*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnEncoderReconfigured(
676*d9f75844SAndroid Build Coastguard Worker const VideoEncoderConfig& config,
677*d9f75844SAndroid Build Coastguard Worker const std::vector<VideoStream>& streams) {
678*d9f75844SAndroid Build Coastguard Worker // Called on VideoStreamEncoder's encoder_queue_.
679*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
680*d9f75844SAndroid Build Coastguard Worker
681*d9f75844SAndroid Build Coastguard Worker if (content_type_ != config.content_type) {
682*d9f75844SAndroid Build Coastguard Worker uma_container_->UpdateHistograms(rtp_config_, stats_);
683*d9f75844SAndroid Build Coastguard Worker uma_container_.reset(new UmaSamplesContainer(
684*d9f75844SAndroid Build Coastguard Worker GetUmaPrefix(config.content_type), stats_, clock_));
685*d9f75844SAndroid Build Coastguard Worker content_type_ = config.content_type;
686*d9f75844SAndroid Build Coastguard Worker }
687*d9f75844SAndroid Build Coastguard Worker uma_container_->encoded_frames_.clear();
688*d9f75844SAndroid Build Coastguard Worker uma_container_->num_streams_ = streams.size();
689*d9f75844SAndroid Build Coastguard Worker uma_container_->num_pixels_highest_stream_ =
690*d9f75844SAndroid Build Coastguard Worker streams.empty() ? 0 : (streams.back().width * streams.back().height);
691*d9f75844SAndroid Build Coastguard Worker }
692*d9f75844SAndroid Build Coastguard Worker
OnEncodedFrameTimeMeasured(int encode_time_ms,int encode_usage_percent)693*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnEncodedFrameTimeMeasured(int encode_time_ms,
694*d9f75844SAndroid Build Coastguard Worker int encode_usage_percent) {
695*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(encode_time_ms, 0);
696*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
697*d9f75844SAndroid Build Coastguard Worker uma_container_->encode_time_counter_.Add(encode_time_ms);
698*d9f75844SAndroid Build Coastguard Worker encode_time_.Apply(1.0f, encode_time_ms);
699*d9f75844SAndroid Build Coastguard Worker stats_.avg_encode_time_ms = std::round(encode_time_.filtered());
700*d9f75844SAndroid Build Coastguard Worker stats_.total_encode_time_ms += encode_time_ms;
701*d9f75844SAndroid Build Coastguard Worker stats_.encode_usage_percent = encode_usage_percent;
702*d9f75844SAndroid Build Coastguard Worker }
703*d9f75844SAndroid Build Coastguard Worker
OnSuspendChange(bool is_suspended)704*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
705*d9f75844SAndroid Build Coastguard Worker int64_t now_ms = clock_->TimeInMilliseconds();
706*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
707*d9f75844SAndroid Build Coastguard Worker stats_.suspended = is_suspended;
708*d9f75844SAndroid Build Coastguard Worker if (is_suspended) {
709*d9f75844SAndroid Build Coastguard Worker // Pause framerate (add min pause time since there may be frames/packets
710*d9f75844SAndroid Build Coastguard Worker // that are not yet sent).
711*d9f75844SAndroid Build Coastguard Worker const int64_t kMinMs = 500;
712*d9f75844SAndroid Build Coastguard Worker uma_container_->input_fps_counter_.ProcessAndPauseForDuration(kMinMs);
713*d9f75844SAndroid Build Coastguard Worker uma_container_->sent_fps_counter_.ProcessAndPauseForDuration(kMinMs);
714*d9f75844SAndroid Build Coastguard Worker // Pause bitrate stats.
715*d9f75844SAndroid Build Coastguard Worker uma_container_->total_byte_counter_.ProcessAndPauseForDuration(kMinMs);
716*d9f75844SAndroid Build Coastguard Worker uma_container_->media_byte_counter_.ProcessAndPauseForDuration(kMinMs);
717*d9f75844SAndroid Build Coastguard Worker uma_container_->rtx_byte_counter_.ProcessAndPauseForDuration(kMinMs);
718*d9f75844SAndroid Build Coastguard Worker uma_container_->padding_byte_counter_.ProcessAndPauseForDuration(kMinMs);
719*d9f75844SAndroid Build Coastguard Worker uma_container_->retransmit_byte_counter_.ProcessAndPauseForDuration(kMinMs);
720*d9f75844SAndroid Build Coastguard Worker uma_container_->fec_byte_counter_.ProcessAndPauseForDuration(kMinMs);
721*d9f75844SAndroid Build Coastguard Worker // Stop adaptation stats.
722*d9f75844SAndroid Build Coastguard Worker uma_container_->cpu_adapt_timer_.Stop(now_ms);
723*d9f75844SAndroid Build Coastguard Worker uma_container_->quality_adapt_timer_.Stop(now_ms);
724*d9f75844SAndroid Build Coastguard Worker } else {
725*d9f75844SAndroid Build Coastguard Worker // Start adaptation stats if scaling is enabled.
726*d9f75844SAndroid Build Coastguard Worker if (adaptation_limitations_.MaskedCpuCounts()
727*d9f75844SAndroid Build Coastguard Worker .resolution_adaptations.has_value())
728*d9f75844SAndroid Build Coastguard Worker uma_container_->cpu_adapt_timer_.Start(now_ms);
729*d9f75844SAndroid Build Coastguard Worker if (adaptation_limitations_.MaskedQualityCounts()
730*d9f75844SAndroid Build Coastguard Worker .resolution_adaptations.has_value())
731*d9f75844SAndroid Build Coastguard Worker uma_container_->quality_adapt_timer_.Start(now_ms);
732*d9f75844SAndroid Build Coastguard Worker // Stop pause explicitly for stats that may be zero/not updated for some
733*d9f75844SAndroid Build Coastguard Worker // time.
734*d9f75844SAndroid Build Coastguard Worker uma_container_->rtx_byte_counter_.ProcessAndStopPause();
735*d9f75844SAndroid Build Coastguard Worker uma_container_->padding_byte_counter_.ProcessAndStopPause();
736*d9f75844SAndroid Build Coastguard Worker uma_container_->retransmit_byte_counter_.ProcessAndStopPause();
737*d9f75844SAndroid Build Coastguard Worker uma_container_->fec_byte_counter_.ProcessAndStopPause();
738*d9f75844SAndroid Build Coastguard Worker }
739*d9f75844SAndroid Build Coastguard Worker }
740*d9f75844SAndroid Build Coastguard Worker
GetStats()741*d9f75844SAndroid Build Coastguard Worker VideoSendStream::Stats SendStatisticsProxy::GetStats() {
742*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
743*d9f75844SAndroid Build Coastguard Worker PurgeOldStats();
744*d9f75844SAndroid Build Coastguard Worker stats_.input_frame_rate =
745*d9f75844SAndroid Build Coastguard Worker uma_container_->input_frame_rate_tracker_.ComputeRate();
746*d9f75844SAndroid Build Coastguard Worker stats_.frames =
747*d9f75844SAndroid Build Coastguard Worker uma_container_->input_frame_rate_tracker_.TotalSampleCount();
748*d9f75844SAndroid Build Coastguard Worker stats_.content_type =
749*d9f75844SAndroid Build Coastguard Worker content_type_ == VideoEncoderConfig::ContentType::kRealtimeVideo
750*d9f75844SAndroid Build Coastguard Worker ? VideoContentType::UNSPECIFIED
751*d9f75844SAndroid Build Coastguard Worker : VideoContentType::SCREENSHARE;
752*d9f75844SAndroid Build Coastguard Worker stats_.encode_frame_rate = round(encoded_frame_rate_tracker_.ComputeRate());
753*d9f75844SAndroid Build Coastguard Worker stats_.media_bitrate_bps = media_byte_rate_tracker_.ComputeRate() * 8;
754*d9f75844SAndroid Build Coastguard Worker stats_.quality_limitation_durations_ms =
755*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_.DurationsMs();
756*d9f75844SAndroid Build Coastguard Worker
757*d9f75844SAndroid Build Coastguard Worker for (auto& substream : stats_.substreams) {
758*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc = substream.first;
759*d9f75844SAndroid Build Coastguard Worker if (encoded_frame_rate_trackers_.count(ssrc) > 0) {
760*d9f75844SAndroid Build Coastguard Worker substream.second.encode_frame_rate =
761*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_trackers_[ssrc]->ComputeRate();
762*d9f75844SAndroid Build Coastguard Worker }
763*d9f75844SAndroid Build Coastguard Worker }
764*d9f75844SAndroid Build Coastguard Worker return stats_;
765*d9f75844SAndroid Build Coastguard Worker }
766*d9f75844SAndroid Build Coastguard Worker
PurgeOldStats()767*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::PurgeOldStats() {
768*d9f75844SAndroid Build Coastguard Worker int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
769*d9f75844SAndroid Build Coastguard Worker for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
770*d9f75844SAndroid Build Coastguard Worker stats_.substreams.begin();
771*d9f75844SAndroid Build Coastguard Worker it != stats_.substreams.end(); ++it) {
772*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc = it->first;
773*d9f75844SAndroid Build Coastguard Worker if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
774*d9f75844SAndroid Build Coastguard Worker it->second.width = 0;
775*d9f75844SAndroid Build Coastguard Worker it->second.height = 0;
776*d9f75844SAndroid Build Coastguard Worker }
777*d9f75844SAndroid Build Coastguard Worker }
778*d9f75844SAndroid Build Coastguard Worker }
779*d9f75844SAndroid Build Coastguard Worker
GetStatsEntry(uint32_t ssrc)780*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
781*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc) {
782*d9f75844SAndroid Build Coastguard Worker std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
783*d9f75844SAndroid Build Coastguard Worker stats_.substreams.find(ssrc);
784*d9f75844SAndroid Build Coastguard Worker if (it != stats_.substreams.end())
785*d9f75844SAndroid Build Coastguard Worker return &it->second;
786*d9f75844SAndroid Build Coastguard Worker
787*d9f75844SAndroid Build Coastguard Worker bool is_media = rtp_config_.IsMediaSsrc(ssrc);
788*d9f75844SAndroid Build Coastguard Worker bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
789*d9f75844SAndroid Build Coastguard Worker ssrc == rtp_config_.flexfec.ssrc;
790*d9f75844SAndroid Build Coastguard Worker bool is_rtx = rtp_config_.IsRtxSsrc(ssrc);
791*d9f75844SAndroid Build Coastguard Worker if (!is_media && !is_flexfec && !is_rtx)
792*d9f75844SAndroid Build Coastguard Worker return nullptr;
793*d9f75844SAndroid Build Coastguard Worker
794*d9f75844SAndroid Build Coastguard Worker // Insert new entry and return ptr.
795*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
796*d9f75844SAndroid Build Coastguard Worker if (is_media) {
797*d9f75844SAndroid Build Coastguard Worker entry->type = VideoSendStream::StreamStats::StreamType::kMedia;
798*d9f75844SAndroid Build Coastguard Worker } else if (is_rtx) {
799*d9f75844SAndroid Build Coastguard Worker entry->type = VideoSendStream::StreamStats::StreamType::kRtx;
800*d9f75844SAndroid Build Coastguard Worker } else if (is_flexfec) {
801*d9f75844SAndroid Build Coastguard Worker entry->type = VideoSendStream::StreamStats::StreamType::kFlexfec;
802*d9f75844SAndroid Build Coastguard Worker } else {
803*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED();
804*d9f75844SAndroid Build Coastguard Worker }
805*d9f75844SAndroid Build Coastguard Worker switch (entry->type) {
806*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kMedia:
807*d9f75844SAndroid Build Coastguard Worker break;
808*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kRtx:
809*d9f75844SAndroid Build Coastguard Worker entry->referenced_media_ssrc =
810*d9f75844SAndroid Build Coastguard Worker rtp_config_.GetMediaSsrcAssociatedWithRtxSsrc(ssrc);
811*d9f75844SAndroid Build Coastguard Worker break;
812*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kFlexfec:
813*d9f75844SAndroid Build Coastguard Worker entry->referenced_media_ssrc =
814*d9f75844SAndroid Build Coastguard Worker rtp_config_.GetMediaSsrcAssociatedWithFlexfecSsrc(ssrc);
815*d9f75844SAndroid Build Coastguard Worker break;
816*d9f75844SAndroid Build Coastguard Worker }
817*d9f75844SAndroid Build Coastguard Worker
818*d9f75844SAndroid Build Coastguard Worker return entry;
819*d9f75844SAndroid Build Coastguard Worker }
820*d9f75844SAndroid Build Coastguard Worker
OnInactiveSsrc(uint32_t ssrc)821*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
822*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
823*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
824*d9f75844SAndroid Build Coastguard Worker if (!stats)
825*d9f75844SAndroid Build Coastguard Worker return;
826*d9f75844SAndroid Build Coastguard Worker
827*d9f75844SAndroid Build Coastguard Worker stats->total_bitrate_bps = 0;
828*d9f75844SAndroid Build Coastguard Worker stats->retransmit_bitrate_bps = 0;
829*d9f75844SAndroid Build Coastguard Worker stats->height = 0;
830*d9f75844SAndroid Build Coastguard Worker stats->width = 0;
831*d9f75844SAndroid Build Coastguard Worker }
832*d9f75844SAndroid Build Coastguard Worker
OnSetEncoderTargetRate(uint32_t bitrate_bps)833*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
834*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
835*d9f75844SAndroid Build Coastguard Worker if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
836*d9f75844SAndroid Build Coastguard Worker return; // Start on first non-zero bitrate, may initially be zero.
837*d9f75844SAndroid Build Coastguard Worker
838*d9f75844SAndroid Build Coastguard Worker int64_t now = clock_->TimeInMilliseconds();
839*d9f75844SAndroid Build Coastguard Worker if (uma_container_->target_rate_updates_.last_ms != -1) {
840*d9f75844SAndroid Build Coastguard Worker bool was_paused = stats_.target_media_bitrate_bps == 0;
841*d9f75844SAndroid Build Coastguard Worker int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
842*d9f75844SAndroid Build Coastguard Worker uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
843*d9f75844SAndroid Build Coastguard Worker
844*d9f75844SAndroid Build Coastguard Worker // Use last to not include update when stream is stopped and video disabled.
845*d9f75844SAndroid Build Coastguard Worker if (uma_container_->target_rate_updates_.last_paused_or_resumed)
846*d9f75844SAndroid Build Coastguard Worker ++uma_container_->target_rate_updates_.pause_resume_events;
847*d9f75844SAndroid Build Coastguard Worker
848*d9f75844SAndroid Build Coastguard Worker // Check if video is paused/resumed.
849*d9f75844SAndroid Build Coastguard Worker uma_container_->target_rate_updates_.last_paused_or_resumed =
850*d9f75844SAndroid Build Coastguard Worker (bitrate_bps == 0) != was_paused;
851*d9f75844SAndroid Build Coastguard Worker }
852*d9f75844SAndroid Build Coastguard Worker uma_container_->target_rate_updates_.last_ms = now;
853*d9f75844SAndroid Build Coastguard Worker
854*d9f75844SAndroid Build Coastguard Worker stats_.target_media_bitrate_bps = bitrate_bps;
855*d9f75844SAndroid Build Coastguard Worker }
856*d9f75844SAndroid Build Coastguard Worker
UpdateEncoderFallbackStats(const CodecSpecificInfo * codec_info,int pixels,int simulcast_index)857*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UpdateEncoderFallbackStats(
858*d9f75844SAndroid Build Coastguard Worker const CodecSpecificInfo* codec_info,
859*d9f75844SAndroid Build Coastguard Worker int pixels,
860*d9f75844SAndroid Build Coastguard Worker int simulcast_index) {
861*d9f75844SAndroid Build Coastguard Worker UpdateFallbackDisabledStats(codec_info, pixels, simulcast_index);
862*d9f75844SAndroid Build Coastguard Worker
863*d9f75844SAndroid Build Coastguard Worker if (!fallback_max_pixels_ || !uma_container_->fallback_info_.is_possible) {
864*d9f75844SAndroid Build Coastguard Worker return;
865*d9f75844SAndroid Build Coastguard Worker }
866*d9f75844SAndroid Build Coastguard Worker
867*d9f75844SAndroid Build Coastguard Worker if (!IsForcedFallbackPossible(codec_info, simulcast_index)) {
868*d9f75844SAndroid Build Coastguard Worker uma_container_->fallback_info_.is_possible = false;
869*d9f75844SAndroid Build Coastguard Worker return;
870*d9f75844SAndroid Build Coastguard Worker }
871*d9f75844SAndroid Build Coastguard Worker
872*d9f75844SAndroid Build Coastguard Worker FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_;
873*d9f75844SAndroid Build Coastguard Worker
874*d9f75844SAndroid Build Coastguard Worker const int64_t now_ms = clock_->TimeInMilliseconds();
875*d9f75844SAndroid Build Coastguard Worker bool is_active = fallback_info->is_active;
876*d9f75844SAndroid Build Coastguard Worker if (encoder_changed_) {
877*d9f75844SAndroid Build Coastguard Worker // Implementation changed.
878*d9f75844SAndroid Build Coastguard Worker const bool last_was_vp8_software =
879*d9f75844SAndroid Build Coastguard Worker encoder_changed_->previous_encoder_implementation == kVp8SwCodecName;
880*d9f75844SAndroid Build Coastguard Worker is_active = encoder_changed_->new_encoder_implementation == kVp8SwCodecName;
881*d9f75844SAndroid Build Coastguard Worker encoder_changed_.reset();
882*d9f75844SAndroid Build Coastguard Worker if (!is_active && !last_was_vp8_software) {
883*d9f75844SAndroid Build Coastguard Worker // First or not a VP8 SW change, update stats on next call.
884*d9f75844SAndroid Build Coastguard Worker return;
885*d9f75844SAndroid Build Coastguard Worker }
886*d9f75844SAndroid Build Coastguard Worker if (is_active && (pixels > *fallback_max_pixels_)) {
887*d9f75844SAndroid Build Coastguard Worker // Pixels should not be above `fallback_max_pixels_`. If above skip to
888*d9f75844SAndroid Build Coastguard Worker // avoid fallbacks due to failure.
889*d9f75844SAndroid Build Coastguard Worker fallback_info->is_possible = false;
890*d9f75844SAndroid Build Coastguard Worker return;
891*d9f75844SAndroid Build Coastguard Worker }
892*d9f75844SAndroid Build Coastguard Worker stats_.has_entered_low_resolution = true;
893*d9f75844SAndroid Build Coastguard Worker ++fallback_info->on_off_events;
894*d9f75844SAndroid Build Coastguard Worker }
895*d9f75844SAndroid Build Coastguard Worker
896*d9f75844SAndroid Build Coastguard Worker if (fallback_info->last_update_ms) {
897*d9f75844SAndroid Build Coastguard Worker int64_t diff_ms = now_ms - *(fallback_info->last_update_ms);
898*d9f75844SAndroid Build Coastguard Worker // If the time diff since last update is greater than `max_frame_diff_ms`,
899*d9f75844SAndroid Build Coastguard Worker // video is considered paused/muted and the change is not included.
900*d9f75844SAndroid Build Coastguard Worker if (diff_ms < fallback_info->max_frame_diff_ms) {
901*d9f75844SAndroid Build Coastguard Worker uma_container_->fallback_active_counter_.Add(fallback_info->is_active,
902*d9f75844SAndroid Build Coastguard Worker diff_ms);
903*d9f75844SAndroid Build Coastguard Worker fallback_info->elapsed_ms += diff_ms;
904*d9f75844SAndroid Build Coastguard Worker }
905*d9f75844SAndroid Build Coastguard Worker }
906*d9f75844SAndroid Build Coastguard Worker fallback_info->is_active = is_active;
907*d9f75844SAndroid Build Coastguard Worker fallback_info->last_update_ms.emplace(now_ms);
908*d9f75844SAndroid Build Coastguard Worker }
909*d9f75844SAndroid Build Coastguard Worker
UpdateFallbackDisabledStats(const CodecSpecificInfo * codec_info,int pixels,int simulcast_index)910*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UpdateFallbackDisabledStats(
911*d9f75844SAndroid Build Coastguard Worker const CodecSpecificInfo* codec_info,
912*d9f75844SAndroid Build Coastguard Worker int pixels,
913*d9f75844SAndroid Build Coastguard Worker int simulcast_index) {
914*d9f75844SAndroid Build Coastguard Worker if (!fallback_max_pixels_disabled_ ||
915*d9f75844SAndroid Build Coastguard Worker !uma_container_->fallback_info_disabled_.is_possible ||
916*d9f75844SAndroid Build Coastguard Worker stats_.has_entered_low_resolution) {
917*d9f75844SAndroid Build Coastguard Worker return;
918*d9f75844SAndroid Build Coastguard Worker }
919*d9f75844SAndroid Build Coastguard Worker
920*d9f75844SAndroid Build Coastguard Worker if (!IsForcedFallbackPossible(codec_info, simulcast_index) ||
921*d9f75844SAndroid Build Coastguard Worker stats_.encoder_implementation_name == kVp8SwCodecName) {
922*d9f75844SAndroid Build Coastguard Worker uma_container_->fallback_info_disabled_.is_possible = false;
923*d9f75844SAndroid Build Coastguard Worker return;
924*d9f75844SAndroid Build Coastguard Worker }
925*d9f75844SAndroid Build Coastguard Worker
926*d9f75844SAndroid Build Coastguard Worker if (pixels <= *fallback_max_pixels_disabled_ ||
927*d9f75844SAndroid Build Coastguard Worker uma_container_->fallback_info_disabled_.min_pixel_limit_reached) {
928*d9f75844SAndroid Build Coastguard Worker stats_.has_entered_low_resolution = true;
929*d9f75844SAndroid Build Coastguard Worker }
930*d9f75844SAndroid Build Coastguard Worker }
931*d9f75844SAndroid Build Coastguard Worker
OnMinPixelLimitReached()932*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnMinPixelLimitReached() {
933*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
934*d9f75844SAndroid Build Coastguard Worker uma_container_->fallback_info_disabled_.min_pixel_limit_reached = true;
935*d9f75844SAndroid Build Coastguard Worker }
936*d9f75844SAndroid Build Coastguard Worker
OnSendEncodedImage(const EncodedImage & encoded_image,const CodecSpecificInfo * codec_info)937*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnSendEncodedImage(
938*d9f75844SAndroid Build Coastguard Worker const EncodedImage& encoded_image,
939*d9f75844SAndroid Build Coastguard Worker const CodecSpecificInfo* codec_info) {
940*d9f75844SAndroid Build Coastguard Worker // Simulcast is used for VP8, H264 and Generic.
941*d9f75844SAndroid Build Coastguard Worker int simulcast_idx =
942*d9f75844SAndroid Build Coastguard Worker (codec_info && (codec_info->codecType == kVideoCodecVP8 ||
943*d9f75844SAndroid Build Coastguard Worker codec_info->codecType == kVideoCodecH264 ||
944*d9f75844SAndroid Build Coastguard Worker codec_info->codecType == kVideoCodecGeneric))
945*d9f75844SAndroid Build Coastguard Worker ? encoded_image.SpatialIndex().value_or(0)
946*d9f75844SAndroid Build Coastguard Worker : 0;
947*d9f75844SAndroid Build Coastguard Worker
948*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
949*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_encoded;
950*d9f75844SAndroid Build Coastguard Worker // The current encode frame rate is based on previously encoded frames.
951*d9f75844SAndroid Build Coastguard Worker double encode_frame_rate = encoded_frame_rate_tracker_.ComputeRate();
952*d9f75844SAndroid Build Coastguard Worker // We assume that less than 1 FPS is not a trustworthy estimate - perhaps we
953*d9f75844SAndroid Build Coastguard Worker // just started encoding for the first time or after a pause. Assuming frame
954*d9f75844SAndroid Build Coastguard Worker // rate is at least 1 FPS is conservative to avoid too large increments.
955*d9f75844SAndroid Build Coastguard Worker if (encode_frame_rate < 1.0)
956*d9f75844SAndroid Build Coastguard Worker encode_frame_rate = 1.0;
957*d9f75844SAndroid Build Coastguard Worker double target_frame_size_bytes =
958*d9f75844SAndroid Build Coastguard Worker stats_.target_media_bitrate_bps / (8.0 * encode_frame_rate);
959*d9f75844SAndroid Build Coastguard Worker // `stats_.target_media_bitrate_bps` is set in
960*d9f75844SAndroid Build Coastguard Worker // SendStatisticsProxy::OnSetEncoderTargetRate.
961*d9f75844SAndroid Build Coastguard Worker stats_.total_encoded_bytes_target += round(target_frame_size_bytes);
962*d9f75844SAndroid Build Coastguard Worker if (codec_info) {
963*d9f75844SAndroid Build Coastguard Worker UpdateEncoderFallbackStats(
964*d9f75844SAndroid Build Coastguard Worker codec_info, encoded_image._encodedWidth * encoded_image._encodedHeight,
965*d9f75844SAndroid Build Coastguard Worker simulcast_idx);
966*d9f75844SAndroid Build Coastguard Worker }
967*d9f75844SAndroid Build Coastguard Worker
968*d9f75844SAndroid Build Coastguard Worker if (static_cast<size_t>(simulcast_idx) >= rtp_config_.ssrcs.size()) {
969*d9f75844SAndroid Build Coastguard Worker RTC_LOG(LS_ERROR) << "Encoded image outside simulcast range ("
970*d9f75844SAndroid Build Coastguard Worker << simulcast_idx << " >= " << rtp_config_.ssrcs.size()
971*d9f75844SAndroid Build Coastguard Worker << ").";
972*d9f75844SAndroid Build Coastguard Worker return;
973*d9f75844SAndroid Build Coastguard Worker }
974*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
975*d9f75844SAndroid Build Coastguard Worker
976*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
977*d9f75844SAndroid Build Coastguard Worker if (!stats)
978*d9f75844SAndroid Build Coastguard Worker return;
979*d9f75844SAndroid Build Coastguard Worker
980*d9f75844SAndroid Build Coastguard Worker if (encoded_frame_rate_trackers_.count(ssrc) == 0) {
981*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_trackers_[ssrc] =
982*d9f75844SAndroid Build Coastguard Worker std::make_unique<rtc::RateTracker>(kBucketSizeMs, kBucketCount);
983*d9f75844SAndroid Build Coastguard Worker }
984*d9f75844SAndroid Build Coastguard Worker
985*d9f75844SAndroid Build Coastguard Worker stats->frames_encoded++;
986*d9f75844SAndroid Build Coastguard Worker stats->total_encode_time_ms += encoded_image.timing_.encode_finish_ms -
987*d9f75844SAndroid Build Coastguard Worker encoded_image.timing_.encode_start_ms;
988*d9f75844SAndroid Build Coastguard Worker // Report resolution of the top spatial layer.
989*d9f75844SAndroid Build Coastguard Worker bool is_top_spatial_layer =
990*d9f75844SAndroid Build Coastguard Worker codec_info == nullptr || codec_info->end_of_picture;
991*d9f75844SAndroid Build Coastguard Worker
992*d9f75844SAndroid Build Coastguard Worker if (!stats->width || !stats->height || is_top_spatial_layer) {
993*d9f75844SAndroid Build Coastguard Worker stats->width = encoded_image._encodedWidth;
994*d9f75844SAndroid Build Coastguard Worker stats->height = encoded_image._encodedHeight;
995*d9f75844SAndroid Build Coastguard Worker update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
996*d9f75844SAndroid Build Coastguard Worker }
997*d9f75844SAndroid Build Coastguard Worker
998*d9f75844SAndroid Build Coastguard Worker uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
999*d9f75844SAndroid Build Coastguard Worker VideoFrameType::kVideoFrameKey);
1000*d9f75844SAndroid Build Coastguard Worker
1001*d9f75844SAndroid Build Coastguard Worker if (encoded_image.qp_ != -1) {
1002*d9f75844SAndroid Build Coastguard Worker if (!stats->qp_sum)
1003*d9f75844SAndroid Build Coastguard Worker stats->qp_sum = 0;
1004*d9f75844SAndroid Build Coastguard Worker *stats->qp_sum += encoded_image.qp_;
1005*d9f75844SAndroid Build Coastguard Worker
1006*d9f75844SAndroid Build Coastguard Worker if (codec_info) {
1007*d9f75844SAndroid Build Coastguard Worker if (codec_info->codecType == kVideoCodecVP8) {
1008*d9f75844SAndroid Build Coastguard Worker int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
1009*d9f75844SAndroid Build Coastguard Worker uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
1010*d9f75844SAndroid Build Coastguard Worker } else if (codec_info->codecType == kVideoCodecVP9) {
1011*d9f75844SAndroid Build Coastguard Worker int spatial_idx = encoded_image.SpatialIndex().value_or(-1);
1012*d9f75844SAndroid Build Coastguard Worker uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
1013*d9f75844SAndroid Build Coastguard Worker } else if (codec_info->codecType == kVideoCodecH264) {
1014*d9f75844SAndroid Build Coastguard Worker int spatial_idx = (rtp_config_.ssrcs.size() == 1) ? -1 : simulcast_idx;
1015*d9f75844SAndroid Build Coastguard Worker uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
1016*d9f75844SAndroid Build Coastguard Worker }
1017*d9f75844SAndroid Build Coastguard Worker }
1018*d9f75844SAndroid Build Coastguard Worker }
1019*d9f75844SAndroid Build Coastguard Worker
1020*d9f75844SAndroid Build Coastguard Worker // If any of the simulcast streams have a huge frame, it should be counted
1021*d9f75844SAndroid Build Coastguard Worker // as a single difficult input frame.
1022*d9f75844SAndroid Build Coastguard Worker // https://w3c.github.io/webrtc-stats/#dom-rtcvideosenderstats-hugeframessent
1023*d9f75844SAndroid Build Coastguard Worker if (encoded_image.timing_.flags & VideoSendTiming::kTriggeredBySize) {
1024*d9f75844SAndroid Build Coastguard Worker ++stats->huge_frames_sent;
1025*d9f75844SAndroid Build Coastguard Worker if (!last_outlier_timestamp_ ||
1026*d9f75844SAndroid Build Coastguard Worker *last_outlier_timestamp_ < encoded_image.capture_time_ms_) {
1027*d9f75844SAndroid Build Coastguard Worker last_outlier_timestamp_.emplace(encoded_image.capture_time_ms_);
1028*d9f75844SAndroid Build Coastguard Worker ++stats_.huge_frames_sent;
1029*d9f75844SAndroid Build Coastguard Worker }
1030*d9f75844SAndroid Build Coastguard Worker }
1031*d9f75844SAndroid Build Coastguard Worker
1032*d9f75844SAndroid Build Coastguard Worker media_byte_rate_tracker_.AddSamples(encoded_image.size());
1033*d9f75844SAndroid Build Coastguard Worker
1034*d9f75844SAndroid Build Coastguard Worker if (uma_container_->InsertEncodedFrame(encoded_image, simulcast_idx)) {
1035*d9f75844SAndroid Build Coastguard Worker // First frame seen with this timestamp, track overall fps.
1036*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_tracker_.AddSamples(1);
1037*d9f75844SAndroid Build Coastguard Worker }
1038*d9f75844SAndroid Build Coastguard Worker // is_top_spatial_layer pertains only to SVC, will always be true for
1039*d9f75844SAndroid Build Coastguard Worker // simulcast.
1040*d9f75844SAndroid Build Coastguard Worker if (is_top_spatial_layer)
1041*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_trackers_[ssrc]->AddSamples(1);
1042*d9f75844SAndroid Build Coastguard Worker
1043*d9f75844SAndroid Build Coastguard Worker absl::optional<int> downscales =
1044*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.MaskedQualityCounts().resolution_adaptations;
1045*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_resolution |=
1046*d9f75844SAndroid Build Coastguard Worker (downscales.has_value() && downscales.value() > 0);
1047*d9f75844SAndroid Build Coastguard Worker
1048*d9f75844SAndroid Build Coastguard Worker if (downscales.has_value()) {
1049*d9f75844SAndroid Build Coastguard Worker uma_container_->quality_limited_frame_counter_.Add(downscales.value() > 0);
1050*d9f75844SAndroid Build Coastguard Worker if (downscales.value() > 0)
1051*d9f75844SAndroid Build Coastguard Worker uma_container_->quality_downscales_counter_.Add(downscales.value());
1052*d9f75844SAndroid Build Coastguard Worker }
1053*d9f75844SAndroid Build Coastguard Worker }
1054*d9f75844SAndroid Build Coastguard Worker
OnEncoderImplementationChanged(EncoderImplementation implementation)1055*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnEncoderImplementationChanged(
1056*d9f75844SAndroid Build Coastguard Worker EncoderImplementation implementation) {
1057*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1058*d9f75844SAndroid Build Coastguard Worker encoder_changed_ = EncoderChangeEvent{stats_.encoder_implementation_name,
1059*d9f75844SAndroid Build Coastguard Worker implementation.name};
1060*d9f75844SAndroid Build Coastguard Worker stats_.encoder_implementation_name = implementation.name;
1061*d9f75844SAndroid Build Coastguard Worker stats_.power_efficient_encoder = implementation.is_hardware_accelerated;
1062*d9f75844SAndroid Build Coastguard Worker }
1063*d9f75844SAndroid Build Coastguard Worker
GetInputFrameRate() const1064*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::GetInputFrameRate() const {
1065*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1066*d9f75844SAndroid Build Coastguard Worker return round(uma_container_->input_frame_rate_tracker_.ComputeRate());
1067*d9f75844SAndroid Build Coastguard Worker }
1068*d9f75844SAndroid Build Coastguard Worker
GetSendFrameRate() const1069*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::GetSendFrameRate() const {
1070*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1071*d9f75844SAndroid Build Coastguard Worker return round(encoded_frame_rate_tracker_.ComputeRate());
1072*d9f75844SAndroid Build Coastguard Worker }
1073*d9f75844SAndroid Build Coastguard Worker
OnIncomingFrame(int width,int height)1074*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
1075*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1076*d9f75844SAndroid Build Coastguard Worker uma_container_->input_frame_rate_tracker_.AddSamples(1);
1077*d9f75844SAndroid Build Coastguard Worker uma_container_->input_fps_counter_.Add(1);
1078*d9f75844SAndroid Build Coastguard Worker uma_container_->input_width_counter_.Add(width);
1079*d9f75844SAndroid Build Coastguard Worker uma_container_->input_height_counter_.Add(height);
1080*d9f75844SAndroid Build Coastguard Worker if (adaptation_limitations_.MaskedCpuCounts()
1081*d9f75844SAndroid Build Coastguard Worker .resolution_adaptations.has_value()) {
1082*d9f75844SAndroid Build Coastguard Worker uma_container_->cpu_limited_frame_counter_.Add(
1083*d9f75844SAndroid Build Coastguard Worker stats_.cpu_limited_resolution);
1084*d9f75844SAndroid Build Coastguard Worker }
1085*d9f75844SAndroid Build Coastguard Worker if (encoded_frame_rate_tracker_.TotalSampleCount() == 0) {
1086*d9f75844SAndroid Build Coastguard Worker // Set start time now instead of when first key frame is encoded to avoid a
1087*d9f75844SAndroid Build Coastguard Worker // too high initial estimate.
1088*d9f75844SAndroid Build Coastguard Worker encoded_frame_rate_tracker_.AddSamples(0);
1089*d9f75844SAndroid Build Coastguard Worker }
1090*d9f75844SAndroid Build Coastguard Worker }
1091*d9f75844SAndroid Build Coastguard Worker
OnFrameDropped(DropReason reason)1092*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnFrameDropped(DropReason reason) {
1093*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1094*d9f75844SAndroid Build Coastguard Worker switch (reason) {
1095*d9f75844SAndroid Build Coastguard Worker case DropReason::kSource:
1096*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_dropped_by_capturer;
1097*d9f75844SAndroid Build Coastguard Worker break;
1098*d9f75844SAndroid Build Coastguard Worker case DropReason::kEncoderQueue:
1099*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_dropped_by_encoder_queue;
1100*d9f75844SAndroid Build Coastguard Worker break;
1101*d9f75844SAndroid Build Coastguard Worker case DropReason::kEncoder:
1102*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_dropped_by_encoder;
1103*d9f75844SAndroid Build Coastguard Worker break;
1104*d9f75844SAndroid Build Coastguard Worker case DropReason::kMediaOptimization:
1105*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_dropped_by_rate_limiter;
1106*d9f75844SAndroid Build Coastguard Worker break;
1107*d9f75844SAndroid Build Coastguard Worker case DropReason::kCongestionWindow:
1108*d9f75844SAndroid Build Coastguard Worker ++stats_.frames_dropped_by_congestion_window;
1109*d9f75844SAndroid Build Coastguard Worker break;
1110*d9f75844SAndroid Build Coastguard Worker }
1111*d9f75844SAndroid Build Coastguard Worker }
1112*d9f75844SAndroid Build Coastguard Worker
ClearAdaptationStats()1113*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::ClearAdaptationStats() {
1114*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1115*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.set_cpu_counts(VideoAdaptationCounters());
1116*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.set_quality_counts(VideoAdaptationCounters());
1117*d9f75844SAndroid Build Coastguard Worker UpdateAdaptationStats();
1118*d9f75844SAndroid Build Coastguard Worker }
1119*d9f75844SAndroid Build Coastguard Worker
UpdateAdaptationSettings(VideoStreamEncoderObserver::AdaptationSettings cpu_settings,VideoStreamEncoderObserver::AdaptationSettings quality_settings)1120*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UpdateAdaptationSettings(
1121*d9f75844SAndroid Build Coastguard Worker VideoStreamEncoderObserver::AdaptationSettings cpu_settings,
1122*d9f75844SAndroid Build Coastguard Worker VideoStreamEncoderObserver::AdaptationSettings quality_settings) {
1123*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1124*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.UpdateMaskingSettings(cpu_settings, quality_settings);
1125*d9f75844SAndroid Build Coastguard Worker SetAdaptTimer(adaptation_limitations_.MaskedCpuCounts(),
1126*d9f75844SAndroid Build Coastguard Worker &uma_container_->cpu_adapt_timer_);
1127*d9f75844SAndroid Build Coastguard Worker SetAdaptTimer(adaptation_limitations_.MaskedQualityCounts(),
1128*d9f75844SAndroid Build Coastguard Worker &uma_container_->quality_adapt_timer_);
1129*d9f75844SAndroid Build Coastguard Worker UpdateAdaptationStats();
1130*d9f75844SAndroid Build Coastguard Worker }
1131*d9f75844SAndroid Build Coastguard Worker
OnAdaptationChanged(VideoAdaptationReason reason,const VideoAdaptationCounters & cpu_counters,const VideoAdaptationCounters & quality_counters)1132*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnAdaptationChanged(
1133*d9f75844SAndroid Build Coastguard Worker VideoAdaptationReason reason,
1134*d9f75844SAndroid Build Coastguard Worker const VideoAdaptationCounters& cpu_counters,
1135*d9f75844SAndroid Build Coastguard Worker const VideoAdaptationCounters& quality_counters) {
1136*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1137*d9f75844SAndroid Build Coastguard Worker
1138*d9f75844SAndroid Build Coastguard Worker MaskedAdaptationCounts receiver =
1139*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.MaskedQualityCounts();
1140*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.set_cpu_counts(cpu_counters);
1141*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.set_quality_counts(quality_counters);
1142*d9f75844SAndroid Build Coastguard Worker switch (reason) {
1143*d9f75844SAndroid Build Coastguard Worker case VideoAdaptationReason::kCpu:
1144*d9f75844SAndroid Build Coastguard Worker ++stats_.number_of_cpu_adapt_changes;
1145*d9f75844SAndroid Build Coastguard Worker break;
1146*d9f75844SAndroid Build Coastguard Worker case VideoAdaptationReason::kQuality:
1147*d9f75844SAndroid Build Coastguard Worker TryUpdateInitialQualityResolutionAdaptUp(
1148*d9f75844SAndroid Build Coastguard Worker receiver.resolution_adaptations,
1149*d9f75844SAndroid Build Coastguard Worker adaptation_limitations_.MaskedQualityCounts().resolution_adaptations);
1150*d9f75844SAndroid Build Coastguard Worker ++stats_.number_of_quality_adapt_changes;
1151*d9f75844SAndroid Build Coastguard Worker break;
1152*d9f75844SAndroid Build Coastguard Worker }
1153*d9f75844SAndroid Build Coastguard Worker UpdateAdaptationStats();
1154*d9f75844SAndroid Build Coastguard Worker }
1155*d9f75844SAndroid Build Coastguard Worker
UpdateAdaptationStats()1156*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::UpdateAdaptationStats() {
1157*d9f75844SAndroid Build Coastguard Worker auto cpu_counts = adaptation_limitations_.MaskedCpuCounts();
1158*d9f75844SAndroid Build Coastguard Worker auto quality_counts = adaptation_limitations_.MaskedQualityCounts();
1159*d9f75844SAndroid Build Coastguard Worker
1160*d9f75844SAndroid Build Coastguard Worker bool is_cpu_limited = cpu_counts.resolution_adaptations > 0 ||
1161*d9f75844SAndroid Build Coastguard Worker cpu_counts.num_framerate_reductions > 0;
1162*d9f75844SAndroid Build Coastguard Worker bool is_bandwidth_limited = quality_counts.resolution_adaptations > 0 ||
1163*d9f75844SAndroid Build Coastguard Worker quality_counts.num_framerate_reductions > 0 ||
1164*d9f75844SAndroid Build Coastguard Worker bw_limited_layers_ || internal_encoder_scaler_;
1165*d9f75844SAndroid Build Coastguard Worker if (is_bandwidth_limited) {
1166*d9f75844SAndroid Build Coastguard Worker // We may be both CPU limited and bandwidth limited at the same time but
1167*d9f75844SAndroid Build Coastguard Worker // there is no way to express this in standardized stats. Heuristically,
1168*d9f75844SAndroid Build Coastguard Worker // bandwidth is more likely to be a limiting factor than CPU, and more
1169*d9f75844SAndroid Build Coastguard Worker // likely to vary over time, so only when we aren't bandwidth limited do we
1170*d9f75844SAndroid Build Coastguard Worker // want to know about our CPU being the bottleneck.
1171*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_.SetReason(
1172*d9f75844SAndroid Build Coastguard Worker QualityLimitationReason::kBandwidth);
1173*d9f75844SAndroid Build Coastguard Worker } else if (is_cpu_limited) {
1174*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_.SetReason(QualityLimitationReason::kCpu);
1175*d9f75844SAndroid Build Coastguard Worker } else {
1176*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_.SetReason(
1177*d9f75844SAndroid Build Coastguard Worker QualityLimitationReason::kNone);
1178*d9f75844SAndroid Build Coastguard Worker }
1179*d9f75844SAndroid Build Coastguard Worker
1180*d9f75844SAndroid Build Coastguard Worker stats_.cpu_limited_resolution = cpu_counts.resolution_adaptations > 0;
1181*d9f75844SAndroid Build Coastguard Worker stats_.cpu_limited_framerate = cpu_counts.num_framerate_reductions > 0;
1182*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_resolution = quality_counts.resolution_adaptations > 0;
1183*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_framerate = quality_counts.num_framerate_reductions > 0;
1184*d9f75844SAndroid Build Coastguard Worker // If bitrate allocator has disabled some layers frame-rate or resolution are
1185*d9f75844SAndroid Build Coastguard Worker // limited depending on the encoder configuration.
1186*d9f75844SAndroid Build Coastguard Worker if (bw_limited_layers_) {
1187*d9f75844SAndroid Build Coastguard Worker switch (content_type_) {
1188*d9f75844SAndroid Build Coastguard Worker case VideoEncoderConfig::ContentType::kRealtimeVideo: {
1189*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_resolution = true;
1190*d9f75844SAndroid Build Coastguard Worker break;
1191*d9f75844SAndroid Build Coastguard Worker }
1192*d9f75844SAndroid Build Coastguard Worker case VideoEncoderConfig::ContentType::kScreen: {
1193*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_framerate = true;
1194*d9f75844SAndroid Build Coastguard Worker break;
1195*d9f75844SAndroid Build Coastguard Worker }
1196*d9f75844SAndroid Build Coastguard Worker }
1197*d9f75844SAndroid Build Coastguard Worker }
1198*d9f75844SAndroid Build Coastguard Worker if (internal_encoder_scaler_) {
1199*d9f75844SAndroid Build Coastguard Worker stats_.bw_limited_resolution = true;
1200*d9f75844SAndroid Build Coastguard Worker }
1201*d9f75844SAndroid Build Coastguard Worker
1202*d9f75844SAndroid Build Coastguard Worker stats_.quality_limitation_reason =
1203*d9f75844SAndroid Build Coastguard Worker quality_limitation_reason_tracker_.current_reason();
1204*d9f75844SAndroid Build Coastguard Worker
1205*d9f75844SAndroid Build Coastguard Worker // `stats_.quality_limitation_durations_ms` depends on the current time
1206*d9f75844SAndroid Build Coastguard Worker // when it is polled; it is updated in SendStatisticsProxy::GetStats().
1207*d9f75844SAndroid Build Coastguard Worker }
1208*d9f75844SAndroid Build Coastguard Worker
OnBitrateAllocationUpdated(const VideoCodec & codec,const VideoBitrateAllocation & allocation)1209*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnBitrateAllocationUpdated(
1210*d9f75844SAndroid Build Coastguard Worker const VideoCodec& codec,
1211*d9f75844SAndroid Build Coastguard Worker const VideoBitrateAllocation& allocation) {
1212*d9f75844SAndroid Build Coastguard Worker int num_spatial_layers = 0;
1213*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < kMaxSpatialLayers; i++) {
1214*d9f75844SAndroid Build Coastguard Worker if (codec.spatialLayers[i].active) {
1215*d9f75844SAndroid Build Coastguard Worker num_spatial_layers++;
1216*d9f75844SAndroid Build Coastguard Worker }
1217*d9f75844SAndroid Build Coastguard Worker }
1218*d9f75844SAndroid Build Coastguard Worker int num_simulcast_streams = 0;
1219*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < kMaxSimulcastStreams; i++) {
1220*d9f75844SAndroid Build Coastguard Worker if (codec.simulcastStream[i].active) {
1221*d9f75844SAndroid Build Coastguard Worker num_simulcast_streams++;
1222*d9f75844SAndroid Build Coastguard Worker }
1223*d9f75844SAndroid Build Coastguard Worker }
1224*d9f75844SAndroid Build Coastguard Worker
1225*d9f75844SAndroid Build Coastguard Worker std::array<bool, kMaxSpatialLayers> spatial_layers;
1226*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < kMaxSpatialLayers; i++) {
1227*d9f75844SAndroid Build Coastguard Worker spatial_layers[i] = (allocation.GetSpatialLayerSum(i) > 0);
1228*d9f75844SAndroid Build Coastguard Worker }
1229*d9f75844SAndroid Build Coastguard Worker
1230*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1231*d9f75844SAndroid Build Coastguard Worker
1232*d9f75844SAndroid Build Coastguard Worker bw_limited_layers_ = allocation.is_bw_limited();
1233*d9f75844SAndroid Build Coastguard Worker UpdateAdaptationStats();
1234*d9f75844SAndroid Build Coastguard Worker
1235*d9f75844SAndroid Build Coastguard Worker if (spatial_layers != last_spatial_layer_use_) {
1236*d9f75844SAndroid Build Coastguard Worker // If the number of spatial layers has changed, the resolution change is
1237*d9f75844SAndroid Build Coastguard Worker // not due to quality limitations, it is because the configuration
1238*d9f75844SAndroid Build Coastguard Worker // changed.
1239*d9f75844SAndroid Build Coastguard Worker if (last_num_spatial_layers_ == num_spatial_layers &&
1240*d9f75844SAndroid Build Coastguard Worker last_num_simulcast_streams_ == num_simulcast_streams) {
1241*d9f75844SAndroid Build Coastguard Worker ++stats_.quality_limitation_resolution_changes;
1242*d9f75844SAndroid Build Coastguard Worker }
1243*d9f75844SAndroid Build Coastguard Worker last_spatial_layer_use_ = spatial_layers;
1244*d9f75844SAndroid Build Coastguard Worker }
1245*d9f75844SAndroid Build Coastguard Worker last_num_spatial_layers_ = num_spatial_layers;
1246*d9f75844SAndroid Build Coastguard Worker last_num_simulcast_streams_ = num_simulcast_streams;
1247*d9f75844SAndroid Build Coastguard Worker }
1248*d9f75844SAndroid Build Coastguard Worker
1249*d9f75844SAndroid Build Coastguard Worker // Informes observer if an internal encoder scaler has reduced video
1250*d9f75844SAndroid Build Coastguard Worker // resolution or not. `is_scaled` is a flag indicating if the video is scaled
1251*d9f75844SAndroid Build Coastguard Worker // down.
OnEncoderInternalScalerUpdate(bool is_scaled)1252*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnEncoderInternalScalerUpdate(bool is_scaled) {
1253*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1254*d9f75844SAndroid Build Coastguard Worker internal_encoder_scaler_ = is_scaled;
1255*d9f75844SAndroid Build Coastguard Worker UpdateAdaptationStats();
1256*d9f75844SAndroid Build Coastguard Worker }
1257*d9f75844SAndroid Build Coastguard Worker
1258*d9f75844SAndroid Build Coastguard Worker // TODO(asapersson): Include fps changes.
OnInitialQualityResolutionAdaptDown()1259*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnInitialQualityResolutionAdaptDown() {
1260*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1261*d9f75844SAndroid Build Coastguard Worker ++uma_container_->initial_quality_changes_.down;
1262*d9f75844SAndroid Build Coastguard Worker }
1263*d9f75844SAndroid Build Coastguard Worker
TryUpdateInitialQualityResolutionAdaptUp(absl::optional<int> old_quality_downscales,absl::optional<int> updated_quality_downscales)1264*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::TryUpdateInitialQualityResolutionAdaptUp(
1265*d9f75844SAndroid Build Coastguard Worker absl::optional<int> old_quality_downscales,
1266*d9f75844SAndroid Build Coastguard Worker absl::optional<int> updated_quality_downscales) {
1267*d9f75844SAndroid Build Coastguard Worker if (uma_container_->initial_quality_changes_.down == 0)
1268*d9f75844SAndroid Build Coastguard Worker return;
1269*d9f75844SAndroid Build Coastguard Worker
1270*d9f75844SAndroid Build Coastguard Worker if (old_quality_downscales.has_value() &&
1271*d9f75844SAndroid Build Coastguard Worker old_quality_downscales.value() > 0 &&
1272*d9f75844SAndroid Build Coastguard Worker updated_quality_downscales.value_or(-1) <
1273*d9f75844SAndroid Build Coastguard Worker old_quality_downscales.value()) {
1274*d9f75844SAndroid Build Coastguard Worker // Adapting up in quality.
1275*d9f75844SAndroid Build Coastguard Worker if (uma_container_->initial_quality_changes_.down >
1276*d9f75844SAndroid Build Coastguard Worker uma_container_->initial_quality_changes_.up) {
1277*d9f75844SAndroid Build Coastguard Worker ++uma_container_->initial_quality_changes_.up;
1278*d9f75844SAndroid Build Coastguard Worker }
1279*d9f75844SAndroid Build Coastguard Worker }
1280*d9f75844SAndroid Build Coastguard Worker }
1281*d9f75844SAndroid Build Coastguard Worker
SetAdaptTimer(const MaskedAdaptationCounts & counts,StatsTimer * timer)1282*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::SetAdaptTimer(const MaskedAdaptationCounts& counts,
1283*d9f75844SAndroid Build Coastguard Worker StatsTimer* timer) {
1284*d9f75844SAndroid Build Coastguard Worker if (counts.resolution_adaptations || counts.num_framerate_reductions) {
1285*d9f75844SAndroid Build Coastguard Worker // Adaptation enabled.
1286*d9f75844SAndroid Build Coastguard Worker if (!stats_.suspended)
1287*d9f75844SAndroid Build Coastguard Worker timer->Start(clock_->TimeInMilliseconds());
1288*d9f75844SAndroid Build Coastguard Worker return;
1289*d9f75844SAndroid Build Coastguard Worker }
1290*d9f75844SAndroid Build Coastguard Worker timer->Stop(clock_->TimeInMilliseconds());
1291*d9f75844SAndroid Build Coastguard Worker }
1292*d9f75844SAndroid Build Coastguard Worker
RtcpPacketTypesCounterUpdated(uint32_t ssrc,const RtcpPacketTypeCounter & packet_counter)1293*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
1294*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc,
1295*d9f75844SAndroid Build Coastguard Worker const RtcpPacketTypeCounter& packet_counter) {
1296*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1297*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
1298*d9f75844SAndroid Build Coastguard Worker if (!stats)
1299*d9f75844SAndroid Build Coastguard Worker return;
1300*d9f75844SAndroid Build Coastguard Worker
1301*d9f75844SAndroid Build Coastguard Worker stats->rtcp_packet_type_counts = packet_counter;
1302*d9f75844SAndroid Build Coastguard Worker if (uma_container_->first_rtcp_stats_time_ms_ == -1)
1303*d9f75844SAndroid Build Coastguard Worker uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
1304*d9f75844SAndroid Build Coastguard Worker }
1305*d9f75844SAndroid Build Coastguard Worker
OnReportBlockDataUpdated(ReportBlockData report_block_data)1306*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::OnReportBlockDataUpdated(
1307*d9f75844SAndroid Build Coastguard Worker ReportBlockData report_block_data) {
1308*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1309*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats =
1310*d9f75844SAndroid Build Coastguard Worker GetStatsEntry(report_block_data.report_block().source_ssrc);
1311*d9f75844SAndroid Build Coastguard Worker if (!stats)
1312*d9f75844SAndroid Build Coastguard Worker return;
1313*d9f75844SAndroid Build Coastguard Worker const RTCPReportBlock& report_block = report_block_data.report_block();
1314*d9f75844SAndroid Build Coastguard Worker uma_container_->report_block_stats_.Store(
1315*d9f75844SAndroid Build Coastguard Worker /*ssrc=*/report_block.source_ssrc,
1316*d9f75844SAndroid Build Coastguard Worker /*packets_lost=*/report_block.packets_lost,
1317*d9f75844SAndroid Build Coastguard Worker /*extended_highest_sequence_number=*/
1318*d9f75844SAndroid Build Coastguard Worker report_block.extended_highest_sequence_number);
1319*d9f75844SAndroid Build Coastguard Worker
1320*d9f75844SAndroid Build Coastguard Worker stats->report_block_data = std::move(report_block_data);
1321*d9f75844SAndroid Build Coastguard Worker }
1322*d9f75844SAndroid Build Coastguard Worker
DataCountersUpdated(const StreamDataCounters & counters,uint32_t ssrc)1323*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::DataCountersUpdated(
1324*d9f75844SAndroid Build Coastguard Worker const StreamDataCounters& counters,
1325*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc) {
1326*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1327*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
1328*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc " << ssrc;
1329*d9f75844SAndroid Build Coastguard Worker
1330*d9f75844SAndroid Build Coastguard Worker if (stats->type == VideoSendStream::StreamStats::StreamType::kFlexfec) {
1331*d9f75844SAndroid Build Coastguard Worker // The same counters are reported for both the media ssrc and flexfec ssrc.
1332*d9f75844SAndroid Build Coastguard Worker // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
1333*d9f75844SAndroid Build Coastguard Worker return;
1334*d9f75844SAndroid Build Coastguard Worker }
1335*d9f75844SAndroid Build Coastguard Worker
1336*d9f75844SAndroid Build Coastguard Worker stats->rtp_stats = counters;
1337*d9f75844SAndroid Build Coastguard Worker if (uma_container_->first_rtp_stats_time_ms_ == -1) {
1338*d9f75844SAndroid Build Coastguard Worker int64_t now_ms = clock_->TimeInMilliseconds();
1339*d9f75844SAndroid Build Coastguard Worker uma_container_->first_rtp_stats_time_ms_ = now_ms;
1340*d9f75844SAndroid Build Coastguard Worker uma_container_->cpu_adapt_timer_.Restart(now_ms);
1341*d9f75844SAndroid Build Coastguard Worker uma_container_->quality_adapt_timer_.Restart(now_ms);
1342*d9f75844SAndroid Build Coastguard Worker }
1343*d9f75844SAndroid Build Coastguard Worker
1344*d9f75844SAndroid Build Coastguard Worker uma_container_->total_byte_counter_.Set(counters.transmitted.TotalBytes(),
1345*d9f75844SAndroid Build Coastguard Worker ssrc);
1346*d9f75844SAndroid Build Coastguard Worker uma_container_->padding_byte_counter_.Set(counters.transmitted.padding_bytes,
1347*d9f75844SAndroid Build Coastguard Worker ssrc);
1348*d9f75844SAndroid Build Coastguard Worker uma_container_->retransmit_byte_counter_.Set(
1349*d9f75844SAndroid Build Coastguard Worker counters.retransmitted.TotalBytes(), ssrc);
1350*d9f75844SAndroid Build Coastguard Worker uma_container_->fec_byte_counter_.Set(counters.fec.TotalBytes(), ssrc);
1351*d9f75844SAndroid Build Coastguard Worker switch (stats->type) {
1352*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kMedia:
1353*d9f75844SAndroid Build Coastguard Worker uma_container_->media_byte_counter_.Set(counters.MediaPayloadBytes(),
1354*d9f75844SAndroid Build Coastguard Worker ssrc);
1355*d9f75844SAndroid Build Coastguard Worker break;
1356*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kRtx:
1357*d9f75844SAndroid Build Coastguard Worker uma_container_->rtx_byte_counter_.Set(counters.transmitted.TotalBytes(),
1358*d9f75844SAndroid Build Coastguard Worker ssrc);
1359*d9f75844SAndroid Build Coastguard Worker break;
1360*d9f75844SAndroid Build Coastguard Worker case VideoSendStream::StreamStats::StreamType::kFlexfec:
1361*d9f75844SAndroid Build Coastguard Worker break;
1362*d9f75844SAndroid Build Coastguard Worker }
1363*d9f75844SAndroid Build Coastguard Worker }
1364*d9f75844SAndroid Build Coastguard Worker
Notify(uint32_t total_bitrate_bps,uint32_t retransmit_bitrate_bps,uint32_t ssrc)1365*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
1366*d9f75844SAndroid Build Coastguard Worker uint32_t retransmit_bitrate_bps,
1367*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc) {
1368*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1369*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
1370*d9f75844SAndroid Build Coastguard Worker if (!stats)
1371*d9f75844SAndroid Build Coastguard Worker return;
1372*d9f75844SAndroid Build Coastguard Worker
1373*d9f75844SAndroid Build Coastguard Worker stats->total_bitrate_bps = total_bitrate_bps;
1374*d9f75844SAndroid Build Coastguard Worker stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
1375*d9f75844SAndroid Build Coastguard Worker }
1376*d9f75844SAndroid Build Coastguard Worker
FrameCountUpdated(const FrameCounts & frame_counts,uint32_t ssrc)1377*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
1378*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc) {
1379*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1380*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
1381*d9f75844SAndroid Build Coastguard Worker if (!stats)
1382*d9f75844SAndroid Build Coastguard Worker return;
1383*d9f75844SAndroid Build Coastguard Worker
1384*d9f75844SAndroid Build Coastguard Worker stats->frame_counts = frame_counts;
1385*d9f75844SAndroid Build Coastguard Worker }
1386*d9f75844SAndroid Build Coastguard Worker
SendSideDelayUpdated(int avg_delay_ms,int max_delay_ms,uint32_t ssrc)1387*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
1388*d9f75844SAndroid Build Coastguard Worker int max_delay_ms,
1389*d9f75844SAndroid Build Coastguard Worker uint32_t ssrc) {
1390*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
1391*d9f75844SAndroid Build Coastguard Worker VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
1392*d9f75844SAndroid Build Coastguard Worker if (!stats)
1393*d9f75844SAndroid Build Coastguard Worker return;
1394*d9f75844SAndroid Build Coastguard Worker stats->avg_delay_ms = avg_delay_ms;
1395*d9f75844SAndroid Build Coastguard Worker stats->max_delay_ms = max_delay_ms;
1396*d9f75844SAndroid Build Coastguard Worker
1397*d9f75844SAndroid Build Coastguard Worker uma_container_->delay_counter_.Add(avg_delay_ms);
1398*d9f75844SAndroid Build Coastguard Worker uma_container_->max_delay_counter_.Add(max_delay_ms);
1399*d9f75844SAndroid Build Coastguard Worker }
1400*d9f75844SAndroid Build Coastguard Worker
Start(int64_t now_ms)1401*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::StatsTimer::Start(int64_t now_ms) {
1402*d9f75844SAndroid Build Coastguard Worker if (start_ms == -1)
1403*d9f75844SAndroid Build Coastguard Worker start_ms = now_ms;
1404*d9f75844SAndroid Build Coastguard Worker }
1405*d9f75844SAndroid Build Coastguard Worker
Stop(int64_t now_ms)1406*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::StatsTimer::Stop(int64_t now_ms) {
1407*d9f75844SAndroid Build Coastguard Worker if (start_ms != -1) {
1408*d9f75844SAndroid Build Coastguard Worker total_ms += now_ms - start_ms;
1409*d9f75844SAndroid Build Coastguard Worker start_ms = -1;
1410*d9f75844SAndroid Build Coastguard Worker }
1411*d9f75844SAndroid Build Coastguard Worker }
1412*d9f75844SAndroid Build Coastguard Worker
Restart(int64_t now_ms)1413*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::StatsTimer::Restart(int64_t now_ms) {
1414*d9f75844SAndroid Build Coastguard Worker total_ms = 0;
1415*d9f75844SAndroid Build Coastguard Worker if (start_ms != -1)
1416*d9f75844SAndroid Build Coastguard Worker start_ms = now_ms;
1417*d9f75844SAndroid Build Coastguard Worker }
1418*d9f75844SAndroid Build Coastguard Worker
Add(int sample)1419*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::SampleCounter::Add(int sample) {
1420*d9f75844SAndroid Build Coastguard Worker sum += sample;
1421*d9f75844SAndroid Build Coastguard Worker ++num_samples;
1422*d9f75844SAndroid Build Coastguard Worker }
1423*d9f75844SAndroid Build Coastguard Worker
Avg(int64_t min_required_samples) const1424*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::SampleCounter::Avg(
1425*d9f75844SAndroid Build Coastguard Worker int64_t min_required_samples) const {
1426*d9f75844SAndroid Build Coastguard Worker if (num_samples < min_required_samples || num_samples == 0)
1427*d9f75844SAndroid Build Coastguard Worker return -1;
1428*d9f75844SAndroid Build Coastguard Worker return static_cast<int>((sum + (num_samples / 2)) / num_samples);
1429*d9f75844SAndroid Build Coastguard Worker }
1430*d9f75844SAndroid Build Coastguard Worker
Add(bool sample)1431*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
1432*d9f75844SAndroid Build Coastguard Worker if (sample)
1433*d9f75844SAndroid Build Coastguard Worker ++sum;
1434*d9f75844SAndroid Build Coastguard Worker ++num_samples;
1435*d9f75844SAndroid Build Coastguard Worker }
1436*d9f75844SAndroid Build Coastguard Worker
Add(bool sample,int64_t count)1437*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
1438*d9f75844SAndroid Build Coastguard Worker if (sample)
1439*d9f75844SAndroid Build Coastguard Worker sum += count;
1440*d9f75844SAndroid Build Coastguard Worker num_samples += count;
1441*d9f75844SAndroid Build Coastguard Worker }
Percent(int64_t min_required_samples) const1442*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::BoolSampleCounter::Percent(
1443*d9f75844SAndroid Build Coastguard Worker int64_t min_required_samples) const {
1444*d9f75844SAndroid Build Coastguard Worker return Fraction(min_required_samples, 100.0f);
1445*d9f75844SAndroid Build Coastguard Worker }
1446*d9f75844SAndroid Build Coastguard Worker
Permille(int64_t min_required_samples) const1447*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::BoolSampleCounter::Permille(
1448*d9f75844SAndroid Build Coastguard Worker int64_t min_required_samples) const {
1449*d9f75844SAndroid Build Coastguard Worker return Fraction(min_required_samples, 1000.0f);
1450*d9f75844SAndroid Build Coastguard Worker }
1451*d9f75844SAndroid Build Coastguard Worker
Fraction(int64_t min_required_samples,float multiplier) const1452*d9f75844SAndroid Build Coastguard Worker int SendStatisticsProxy::BoolSampleCounter::Fraction(
1453*d9f75844SAndroid Build Coastguard Worker int64_t min_required_samples,
1454*d9f75844SAndroid Build Coastguard Worker float multiplier) const {
1455*d9f75844SAndroid Build Coastguard Worker if (num_samples < min_required_samples || num_samples == 0)
1456*d9f75844SAndroid Build Coastguard Worker return -1;
1457*d9f75844SAndroid Build Coastguard Worker return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
1458*d9f75844SAndroid Build Coastguard Worker }
1459*d9f75844SAndroid Build Coastguard Worker
1460*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::MaskedAdaptationCounts
MaskedCpuCounts() const1461*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::Adaptations::MaskedCpuCounts() const {
1462*d9f75844SAndroid Build Coastguard Worker return Mask(cpu_counts_, cpu_settings_);
1463*d9f75844SAndroid Build Coastguard Worker }
1464*d9f75844SAndroid Build Coastguard Worker
1465*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::MaskedAdaptationCounts
MaskedQualityCounts() const1466*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::Adaptations::MaskedQualityCounts() const {
1467*d9f75844SAndroid Build Coastguard Worker return Mask(quality_counts_, quality_settings_);
1468*d9f75844SAndroid Build Coastguard Worker }
1469*d9f75844SAndroid Build Coastguard Worker
set_cpu_counts(const VideoAdaptationCounters & cpu_counts)1470*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::Adaptations::set_cpu_counts(
1471*d9f75844SAndroid Build Coastguard Worker const VideoAdaptationCounters& cpu_counts) {
1472*d9f75844SAndroid Build Coastguard Worker cpu_counts_ = cpu_counts;
1473*d9f75844SAndroid Build Coastguard Worker }
1474*d9f75844SAndroid Build Coastguard Worker
set_quality_counts(const VideoAdaptationCounters & quality_counts)1475*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::Adaptations::set_quality_counts(
1476*d9f75844SAndroid Build Coastguard Worker const VideoAdaptationCounters& quality_counts) {
1477*d9f75844SAndroid Build Coastguard Worker quality_counts_ = quality_counts;
1478*d9f75844SAndroid Build Coastguard Worker }
1479*d9f75844SAndroid Build Coastguard Worker
cpu_counts() const1480*d9f75844SAndroid Build Coastguard Worker VideoAdaptationCounters SendStatisticsProxy::Adaptations::cpu_counts() const {
1481*d9f75844SAndroid Build Coastguard Worker return cpu_counts_;
1482*d9f75844SAndroid Build Coastguard Worker }
1483*d9f75844SAndroid Build Coastguard Worker
quality_counts() const1484*d9f75844SAndroid Build Coastguard Worker VideoAdaptationCounters SendStatisticsProxy::Adaptations::quality_counts()
1485*d9f75844SAndroid Build Coastguard Worker const {
1486*d9f75844SAndroid Build Coastguard Worker return quality_counts_;
1487*d9f75844SAndroid Build Coastguard Worker }
1488*d9f75844SAndroid Build Coastguard Worker
UpdateMaskingSettings(VideoStreamEncoderObserver::AdaptationSettings cpu_settings,VideoStreamEncoderObserver::AdaptationSettings quality_settings)1489*d9f75844SAndroid Build Coastguard Worker void SendStatisticsProxy::Adaptations::UpdateMaskingSettings(
1490*d9f75844SAndroid Build Coastguard Worker VideoStreamEncoderObserver::AdaptationSettings cpu_settings,
1491*d9f75844SAndroid Build Coastguard Worker VideoStreamEncoderObserver::AdaptationSettings quality_settings) {
1492*d9f75844SAndroid Build Coastguard Worker cpu_settings_ = std::move(cpu_settings);
1493*d9f75844SAndroid Build Coastguard Worker quality_settings_ = std::move(quality_settings);
1494*d9f75844SAndroid Build Coastguard Worker }
1495*d9f75844SAndroid Build Coastguard Worker
1496*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::MaskedAdaptationCounts
Mask(const VideoAdaptationCounters & counters,const VideoStreamEncoderObserver::AdaptationSettings & settings) const1497*d9f75844SAndroid Build Coastguard Worker SendStatisticsProxy::Adaptations::Mask(
1498*d9f75844SAndroid Build Coastguard Worker const VideoAdaptationCounters& counters,
1499*d9f75844SAndroid Build Coastguard Worker const VideoStreamEncoderObserver::AdaptationSettings& settings) const {
1500*d9f75844SAndroid Build Coastguard Worker MaskedAdaptationCounts masked_counts;
1501*d9f75844SAndroid Build Coastguard Worker if (settings.resolution_scaling_enabled) {
1502*d9f75844SAndroid Build Coastguard Worker masked_counts.resolution_adaptations = counters.resolution_adaptations;
1503*d9f75844SAndroid Build Coastguard Worker }
1504*d9f75844SAndroid Build Coastguard Worker if (settings.framerate_scaling_enabled) {
1505*d9f75844SAndroid Build Coastguard Worker masked_counts.num_framerate_reductions = counters.fps_adaptations;
1506*d9f75844SAndroid Build Coastguard Worker }
1507*d9f75844SAndroid Build Coastguard Worker return masked_counts;
1508*d9f75844SAndroid Build Coastguard Worker }
1509*d9f75844SAndroid Build Coastguard Worker
1510*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
1511