Line data Source code
1 : /*
2 : * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 : *
4 : * Use of this source code is governed by a BSD-style license
5 : * that can be found in the LICENSE file in the root of the source
6 : * tree. An additional intellectual property rights grant can be found
7 : * in the file PATENTS. All contributing project authors may
8 : * be found in the AUTHORS file in the root of the source tree.
9 : */
10 :
11 : #include "webrtc/video/send_statistics_proxy.h"
12 :
13 : #include <algorithm>
14 : #include <cmath>
15 : #include <map>
16 : #include <vector>
17 :
18 : #include "webrtc/base/checks.h"
19 : #include "webrtc/base/logging.h"
20 : #include "webrtc/modules/video_coding/include/video_codec_interface.h"
21 : #include "webrtc/system_wrappers/include/metrics.h"
22 :
23 : namespace webrtc {
24 : namespace {
25 : const float kEncodeTimeWeigthFactor = 0.5f;
26 :
27 : // Used by histograms. Values of entries should not be changed.
28 : enum HistogramCodecType {
29 : kVideoUnknown = 0,
30 : kVideoVp8 = 1,
31 : kVideoVp9 = 2,
32 : kVideoH264 = 3,
33 : kVideoMax = 64,
34 : };
35 :
36 : const char* kRealtimePrefix = "WebRTC.Video.";
37 : const char* kScreenPrefix = "WebRTC.Video.Screenshare.";
38 :
39 0 : const char* GetUmaPrefix(VideoEncoderConfig::ContentType content_type) {
40 0 : switch (content_type) {
41 : case VideoEncoderConfig::ContentType::kRealtimeVideo:
42 0 : return kRealtimePrefix;
43 : case VideoEncoderConfig::ContentType::kScreen:
44 0 : return kScreenPrefix;
45 : }
46 0 : RTC_NOTREACHED();
47 0 : return nullptr;
48 : }
49 :
50 0 : HistogramCodecType PayloadNameToHistogramCodecType(
51 : const std::string& payload_name) {
52 0 : if (payload_name == "VP8") {
53 0 : return kVideoVp8;
54 0 : } else if (payload_name == "VP9") {
55 0 : return kVideoVp9;
56 0 : } else if (payload_name == "H264") {
57 0 : return kVideoH264;
58 : } else {
59 0 : return kVideoUnknown;
60 : }
61 : }
62 :
63 0 : void UpdateCodecTypeHistogram(const std::string& payload_name) {
64 0 : RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType",
65 : PayloadNameToHistogramCodecType(payload_name),
66 : kVideoMax);
67 0 : }
68 : } // namespace
69 :
70 :
71 : const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
72 :
73 0 : SendStatisticsProxy::SendStatisticsProxy(
74 : Clock* clock,
75 : const VideoSendStream::Config& config,
76 0 : VideoEncoderConfig::ContentType content_type)
77 : : clock_(clock),
78 : payload_name_(config.encoder_settings.payload_name),
79 : rtp_config_(config.rtp),
80 : content_type_(content_type),
81 0 : start_ms_(clock->TimeInMilliseconds()),
82 : last_sent_frame_timestamp_(0),
83 : encode_time_(kEncodeTimeWeigthFactor),
84 : uma_container_(
85 0 : new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) {
86 0 : }
87 :
88 0 : SendStatisticsProxy::~SendStatisticsProxy() {
89 0 : rtc::CritScope lock(&crit_);
90 0 : uma_container_->UpdateHistograms(rtp_config_, stats_);
91 :
92 0 : int64_t elapsed_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
93 0 : RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.SendStreamLifetimeInSeconds",
94 : elapsed_sec);
95 :
96 0 : if (elapsed_sec >= metrics::kMinRunTimeInSeconds)
97 0 : UpdateCodecTypeHistogram(payload_name_);
98 0 : }
99 :
100 0 : SendStatisticsProxy::UmaSamplesContainer::UmaSamplesContainer(
101 : const char* prefix,
102 : const VideoSendStream::Stats& stats,
103 0 : Clock* const clock)
104 : : uma_prefix_(prefix),
105 : clock_(clock),
106 : max_sent_width_per_timestamp_(0),
107 : max_sent_height_per_timestamp_(0),
108 : input_frame_rate_tracker_(100, 10u),
109 : input_fps_counter_(clock, nullptr, true),
110 : sent_fps_counter_(clock, nullptr, true),
111 : first_rtcp_stats_time_ms_(-1),
112 : first_rtp_stats_time_ms_(-1),
113 0 : start_stats_(stats) {}
114 :
115 0 : SendStatisticsProxy::UmaSamplesContainer::~UmaSamplesContainer() {}
116 :
117 0 : void AccumulateRtxStats(const VideoSendStream::Stats& stats,
118 : const std::vector<uint32_t>& rtx_ssrcs,
119 : StreamDataCounters* total_rtp_stats,
120 : StreamDataCounters* rtx_stats) {
121 0 : for (auto it : stats.substreams) {
122 0 : if (std::find(rtx_ssrcs.begin(), rtx_ssrcs.end(), it.first) !=
123 0 : rtx_ssrcs.end()) {
124 0 : rtx_stats->Add(it.second.rtp_stats);
125 : } else {
126 0 : total_rtp_stats->Add(it.second.rtp_stats);
127 : }
128 : }
129 0 : }
130 :
131 0 : void SendStatisticsProxy::UmaSamplesContainer::UpdateHistograms(
132 : const VideoSendStream::Config::Rtp& rtp_config,
133 : const VideoSendStream::Stats& current_stats) {
134 0 : RTC_DCHECK(uma_prefix_ == kRealtimePrefix || uma_prefix_ == kScreenPrefix);
135 0 : const int kIndex = uma_prefix_ == kScreenPrefix ? 1 : 0;
136 0 : const int kMinRequiredPeriodicSamples = 6;
137 0 : int in_width = input_width_counter_.Avg(kMinRequiredMetricsSamples);
138 0 : int in_height = input_height_counter_.Avg(kMinRequiredMetricsSamples);
139 0 : if (in_width != -1) {
140 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputWidthInPixels",
141 : in_width);
142 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "InputHeightInPixels",
143 : in_height);
144 : }
145 0 : AggregatedStats in_fps = input_fps_counter_.GetStats();
146 0 : if (in_fps.num_samples >= kMinRequiredPeriodicSamples) {
147 0 : RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "InputFramesPerSecond",
148 : in_fps.average);
149 0 : LOG(LS_INFO) << uma_prefix_ + "InputFramesPerSecond, " << in_fps.ToString();
150 : }
151 :
152 0 : int sent_width = sent_width_counter_.Avg(kMinRequiredMetricsSamples);
153 0 : int sent_height = sent_height_counter_.Avg(kMinRequiredMetricsSamples);
154 0 : if (sent_width != -1) {
155 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentWidthInPixels",
156 : sent_width);
157 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "SentHeightInPixels",
158 : sent_height);
159 : }
160 0 : AggregatedStats sent_fps = sent_fps_counter_.GetStats();
161 0 : if (sent_fps.num_samples >= kMinRequiredPeriodicSamples) {
162 0 : RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "SentFramesPerSecond",
163 : sent_fps.average);
164 0 : LOG(LS_INFO) << uma_prefix_ + "SentFramesPerSecond, "
165 0 : << sent_fps.ToString();
166 : }
167 :
168 0 : int encode_ms = encode_time_counter_.Avg(kMinRequiredMetricsSamples);
169 0 : if (encode_ms != -1) {
170 0 : RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "EncodeTimeInMs",
171 : encode_ms);
172 : }
173 : int key_frames_permille =
174 0 : key_frame_counter_.Permille(kMinRequiredMetricsSamples);
175 0 : if (key_frames_permille != -1) {
176 0 : RTC_HISTOGRAMS_COUNTS_1000(kIndex, uma_prefix_ + "KeyFramesSentInPermille",
177 : key_frames_permille);
178 : }
179 : int quality_limited =
180 0 : quality_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
181 0 : if (quality_limited != -1) {
182 0 : RTC_HISTOGRAMS_PERCENTAGE(kIndex,
183 : uma_prefix_ + "QualityLimitedResolutionInPercent",
184 : quality_limited);
185 : }
186 0 : int downscales = quality_downscales_counter_.Avg(kMinRequiredMetricsSamples);
187 0 : if (downscales != -1) {
188 0 : RTC_HISTOGRAMS_ENUMERATION(
189 : kIndex, uma_prefix_ + "QualityLimitedResolutionDownscales", downscales,
190 : 20);
191 : }
192 : int cpu_limited =
193 0 : cpu_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
194 0 : if (cpu_limited != -1) {
195 0 : RTC_HISTOGRAMS_PERCENTAGE(
196 : kIndex, uma_prefix_ + "CpuLimitedResolutionInPercent", cpu_limited);
197 : }
198 : int bw_limited =
199 0 : bw_limited_frame_counter_.Percent(kMinRequiredMetricsSamples);
200 0 : if (bw_limited != -1) {
201 0 : RTC_HISTOGRAMS_PERCENTAGE(
202 : kIndex, uma_prefix_ + "BandwidthLimitedResolutionInPercent",
203 : bw_limited);
204 : }
205 : int num_disabled =
206 0 : bw_resolutions_disabled_counter_.Avg(kMinRequiredMetricsSamples);
207 0 : if (num_disabled != -1) {
208 0 : RTC_HISTOGRAMS_ENUMERATION(
209 : kIndex, uma_prefix_ + "BandwidthLimitedResolutionsDisabled",
210 : num_disabled, 10);
211 : }
212 0 : int delay_ms = delay_counter_.Avg(kMinRequiredMetricsSamples);
213 0 : if (delay_ms != -1)
214 0 : RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayInMs",
215 : delay_ms);
216 :
217 0 : int max_delay_ms = max_delay_counter_.Avg(kMinRequiredMetricsSamples);
218 0 : if (max_delay_ms != -1) {
219 0 : RTC_HISTOGRAMS_COUNTS_100000(kIndex, uma_prefix_ + "SendSideDelayMaxInMs",
220 : max_delay_ms);
221 : }
222 :
223 0 : for (const auto& it : qp_counters_) {
224 0 : int qp_vp8 = it.second.vp8.Avg(kMinRequiredMetricsSamples);
225 0 : if (qp_vp8 != -1) {
226 0 : int spatial_idx = it.first;
227 0 : if (spatial_idx == -1) {
228 0 : RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8",
229 : qp_vp8);
230 0 : } else if (spatial_idx == 0) {
231 0 : RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S0",
232 : qp_vp8);
233 0 : } else if (spatial_idx == 1) {
234 0 : RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S1",
235 : qp_vp8);
236 0 : } else if (spatial_idx == 2) {
237 0 : RTC_HISTOGRAMS_COUNTS_200(kIndex, uma_prefix_ + "Encoded.Qp.Vp8.S2",
238 : qp_vp8);
239 : } else {
240 0 : LOG(LS_WARNING) << "QP stats not recorded for VP8 spatial idx "
241 0 : << spatial_idx;
242 : }
243 : }
244 0 : int qp_vp9 = it.second.vp9.Avg(kMinRequiredMetricsSamples);
245 0 : if (qp_vp9 != -1) {
246 0 : int spatial_idx = it.first;
247 0 : if (spatial_idx == -1) {
248 0 : RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9",
249 : qp_vp9);
250 0 : } else if (spatial_idx == 0) {
251 0 : RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S0",
252 : qp_vp9);
253 0 : } else if (spatial_idx == 1) {
254 0 : RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S1",
255 : qp_vp9);
256 0 : } else if (spatial_idx == 2) {
257 0 : RTC_HISTOGRAMS_COUNTS_500(kIndex, uma_prefix_ + "Encoded.Qp.Vp9.S2",
258 : qp_vp9);
259 : } else {
260 0 : LOG(LS_WARNING) << "QP stats not recorded for VP9 spatial layer "
261 0 : << spatial_idx;
262 : }
263 : }
264 0 : int qp_h264 = it.second.h264.Avg(kMinRequiredMetricsSamples);
265 0 : if (qp_h264 != -1) {
266 0 : int spatial_idx = it.first;
267 0 : RTC_DCHECK_EQ(-1, spatial_idx);
268 0 : RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "Encoded.Qp.H264",
269 : qp_h264);
270 : }
271 : }
272 :
273 0 : if (first_rtcp_stats_time_ms_ != -1) {
274 : int64_t elapsed_sec =
275 0 : (clock_->TimeInMilliseconds() - first_rtcp_stats_time_ms_) / 1000;
276 0 : if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
277 0 : int fraction_lost = report_block_stats_.FractionLostInPercent();
278 0 : if (fraction_lost != -1) {
279 0 : RTC_HISTOGRAMS_PERCENTAGE(
280 : kIndex, uma_prefix_ + "SentPacketsLostInPercent", fraction_lost);
281 : }
282 :
283 : // The RTCP packet type counters, delivered via the
284 : // RtcpPacketTypeCounterObserver interface, are aggregates over the entire
285 : // life of the send stream and are not reset when switching content type.
286 : // For the purpose of these statistics though, we want new counts when
287 : // switching since we switch histogram name. On every reset of the
288 : // UmaSamplesContainer, we save the initial state of the counters, so that
289 : // we can calculate the delta here and aggregate over all ssrcs.
290 0 : RtcpPacketTypeCounter counters;
291 0 : for (uint32_t ssrc : rtp_config.ssrcs) {
292 0 : auto kv = current_stats.substreams.find(ssrc);
293 0 : if (kv == current_stats.substreams.end())
294 0 : continue;
295 :
296 : RtcpPacketTypeCounter stream_counters =
297 0 : kv->second.rtcp_packet_type_counts;
298 0 : kv = start_stats_.substreams.find(ssrc);
299 0 : if (kv != start_stats_.substreams.end())
300 0 : stream_counters.Subtract(kv->second.rtcp_packet_type_counts);
301 :
302 0 : counters.Add(stream_counters);
303 : }
304 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex,
305 : uma_prefix_ + "NackPacketsReceivedPerMinute",
306 : counters.nack_packets * 60 / elapsed_sec);
307 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex,
308 : uma_prefix_ + "FirPacketsReceivedPerMinute",
309 : counters.fir_packets * 60 / elapsed_sec);
310 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex,
311 : uma_prefix_ + "PliPacketsReceivedPerMinute",
312 : counters.pli_packets * 60 / elapsed_sec);
313 0 : if (counters.nack_requests > 0) {
314 0 : RTC_HISTOGRAMS_PERCENTAGE(
315 : kIndex, uma_prefix_ + "UniqueNackRequestsReceivedInPercent",
316 : counters.UniqueNackRequestsInPercent());
317 : }
318 : }
319 : }
320 :
321 0 : if (first_rtp_stats_time_ms_ != -1) {
322 : int64_t elapsed_sec =
323 0 : (clock_->TimeInMilliseconds() - first_rtp_stats_time_ms_) / 1000;
324 0 : if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
325 0 : RTC_HISTOGRAMS_COUNTS_100(kIndex, uma_prefix_ + "NumberOfPauseEvents",
326 : target_rate_updates_.pause_resume_events);
327 :
328 : int paused_time_percent =
329 0 : paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000);
330 0 : if (paused_time_percent != -1) {
331 0 : RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent",
332 : paused_time_percent);
333 : }
334 :
335 0 : StreamDataCounters rtp;
336 0 : StreamDataCounters rtx;
337 0 : AccumulateRtxStats(current_stats, rtp_config.rtx.ssrcs, &rtp, &rtx);
338 0 : StreamDataCounters start_rtp;
339 0 : StreamDataCounters start_rtx;
340 0 : AccumulateRtxStats(start_stats_, rtp_config.rtx.ssrcs, &start_rtp,
341 0 : &start_rtx);
342 0 : rtp.Subtract(start_rtp);
343 0 : rtx.Subtract(start_rtx);
344 0 : StreamDataCounters rtp_rtx = rtp;
345 0 : rtp_rtx.Add(rtx);
346 :
347 0 : RTC_HISTOGRAMS_COUNTS_10000(
348 : kIndex, uma_prefix_ + "BitrateSentInKbps",
349 : static_cast<int>(rtp_rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
350 : 1000));
351 0 : RTC_HISTOGRAMS_COUNTS_10000(
352 : kIndex, uma_prefix_ + "MediaBitrateSentInKbps",
353 : static_cast<int>(rtp.MediaPayloadBytes() * 8 / elapsed_sec / 1000));
354 0 : RTC_HISTOGRAMS_COUNTS_10000(
355 : kIndex, uma_prefix_ + "PaddingBitrateSentInKbps",
356 : static_cast<int>(rtp_rtx.transmitted.padding_bytes * 8 / elapsed_sec /
357 : 1000));
358 0 : RTC_HISTOGRAMS_COUNTS_10000(
359 : kIndex, uma_prefix_ + "RetransmittedBitrateSentInKbps",
360 : static_cast<int>(rtp_rtx.retransmitted.TotalBytes() * 8 /
361 : elapsed_sec / 1000));
362 0 : if (!rtp_config.rtx.ssrcs.empty()) {
363 0 : RTC_HISTOGRAMS_COUNTS_10000(
364 : kIndex, uma_prefix_ + "RtxBitrateSentInKbps",
365 : static_cast<int>(rtx.transmitted.TotalBytes() * 8 / elapsed_sec /
366 : 1000));
367 : }
368 0 : if (rtp_config.flexfec.payload_type != -1 ||
369 0 : rtp_config.ulpfec.red_payload_type != -1) {
370 0 : RTC_HISTOGRAMS_COUNTS_10000(kIndex,
371 : uma_prefix_ + "FecBitrateSentInKbps",
372 : static_cast<int>(rtp_rtx.fec.TotalBytes() *
373 : 8 / elapsed_sec / 1000));
374 : }
375 : }
376 : }
377 0 : }
378 :
379 0 : void SendStatisticsProxy::OnEncoderReconfigured(
380 : const VideoEncoderConfig& config,
381 : uint32_t preferred_bitrate_bps) {
382 0 : rtc::CritScope lock(&crit_);
383 0 : stats_.preferred_media_bitrate_bps = preferred_bitrate_bps;
384 :
385 0 : if (content_type_ != config.content_type) {
386 0 : uma_container_->UpdateHistograms(rtp_config_, stats_);
387 0 : uma_container_.reset(new UmaSamplesContainer(
388 0 : GetUmaPrefix(config.content_type), stats_, clock_));
389 0 : content_type_ = config.content_type;
390 : }
391 0 : }
392 :
393 0 : void SendStatisticsProxy::OnEncoderStatsUpdate(uint32_t framerate,
394 : uint32_t bitrate) {
395 0 : rtc::CritScope lock(&crit_);
396 0 : stats_.encode_frame_rate = framerate;
397 0 : stats_.media_bitrate_bps = bitrate;
398 0 : }
399 :
400 0 : void SendStatisticsProxy::OnEncodedFrameTimeMeasured(
401 : int encode_time_ms,
402 : const CpuOveruseMetrics& metrics) {
403 0 : rtc::CritScope lock(&crit_);
404 0 : uma_container_->encode_time_counter_.Add(encode_time_ms);
405 0 : encode_time_.Apply(1.0f, encode_time_ms);
406 0 : stats_.avg_encode_time_ms = round(encode_time_.filtered());
407 0 : stats_.encode_usage_percent = metrics.encode_usage_percent;
408 0 : }
409 :
410 0 : void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
411 0 : rtc::CritScope lock(&crit_);
412 0 : stats_.suspended = is_suspended;
413 : // Pause framerate stats.
414 0 : if (is_suspended) {
415 0 : uma_container_->input_fps_counter_.ProcessAndPause();
416 0 : uma_container_->sent_fps_counter_.ProcessAndPause();
417 : }
418 0 : }
419 :
420 0 : VideoSendStream::Stats SendStatisticsProxy::GetStats() {
421 0 : rtc::CritScope lock(&crit_);
422 0 : PurgeOldStats();
423 0 : stats_.input_frame_rate =
424 0 : round(uma_container_->input_frame_rate_tracker_.ComputeRate());
425 0 : return stats_;
426 : }
427 :
428 0 : void SendStatisticsProxy::PurgeOldStats() {
429 0 : int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
430 0 : for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
431 0 : stats_.substreams.begin();
432 0 : it != stats_.substreams.end(); ++it) {
433 0 : uint32_t ssrc = it->first;
434 0 : if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
435 0 : it->second.width = 0;
436 0 : it->second.height = 0;
437 : }
438 : }
439 0 : }
440 :
441 0 : VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
442 : uint32_t ssrc) {
443 : std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
444 0 : stats_.substreams.find(ssrc);
445 0 : if (it != stats_.substreams.end())
446 0 : return &it->second;
447 :
448 0 : bool is_media = std::find(rtp_config_.ssrcs.begin(), rtp_config_.ssrcs.end(),
449 0 : ssrc) != rtp_config_.ssrcs.end();
450 0 : bool is_flexfec = rtp_config_.flexfec.payload_type != -1 &&
451 0 : ssrc == rtp_config_.flexfec.ssrc;
452 : bool is_rtx =
453 0 : std::find(rtp_config_.rtx.ssrcs.begin(), rtp_config_.rtx.ssrcs.end(),
454 0 : ssrc) != rtp_config_.rtx.ssrcs.end();
455 0 : if (!is_media && !is_flexfec && !is_rtx)
456 0 : return nullptr;
457 :
458 : // Insert new entry and return ptr.
459 0 : VideoSendStream::StreamStats* entry = &stats_.substreams[ssrc];
460 0 : entry->is_rtx = is_rtx;
461 0 : entry->is_flexfec = is_flexfec;
462 :
463 0 : return entry;
464 : }
465 :
466 0 : void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
467 0 : rtc::CritScope lock(&crit_);
468 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
469 0 : if (!stats)
470 0 : return;
471 :
472 0 : stats->total_bitrate_bps = 0;
473 0 : stats->retransmit_bitrate_bps = 0;
474 0 : stats->height = 0;
475 0 : stats->width = 0;
476 : }
477 :
478 0 : void SendStatisticsProxy::OnSetEncoderTargetRate(uint32_t bitrate_bps) {
479 0 : rtc::CritScope lock(&crit_);
480 0 : if (uma_container_->target_rate_updates_.last_ms == -1 && bitrate_bps == 0)
481 0 : return; // Start on first non-zero bitrate, may initially be zero.
482 :
483 0 : int64_t now = clock_->TimeInMilliseconds();
484 0 : if (uma_container_->target_rate_updates_.last_ms != -1) {
485 0 : bool was_paused = stats_.target_media_bitrate_bps == 0;
486 0 : int64_t diff_ms = now - uma_container_->target_rate_updates_.last_ms;
487 0 : uma_container_->paused_time_counter_.Add(was_paused, diff_ms);
488 :
489 : // Use last to not include update when stream is stopped and video disabled.
490 0 : if (uma_container_->target_rate_updates_.last_paused_or_resumed)
491 0 : ++uma_container_->target_rate_updates_.pause_resume_events;
492 :
493 : // Check if video is paused/resumed.
494 0 : uma_container_->target_rate_updates_.last_paused_or_resumed =
495 0 : (bitrate_bps == 0) != was_paused;
496 : }
497 0 : uma_container_->target_rate_updates_.last_ms = now;
498 :
499 0 : stats_.target_media_bitrate_bps = bitrate_bps;
500 : }
501 :
502 0 : void SendStatisticsProxy::OnSendEncodedImage(
503 : const EncodedImage& encoded_image,
504 : const CodecSpecificInfo* codec_info) {
505 0 : size_t simulcast_idx = 0;
506 :
507 0 : rtc::CritScope lock(&crit_);
508 0 : ++stats_.frames_encoded;
509 0 : if (codec_info) {
510 0 : if (codec_info->codecType == kVideoCodecVP8) {
511 0 : simulcast_idx = codec_info->codecSpecific.VP8.simulcastIdx;
512 0 : } else if (codec_info->codecType == kVideoCodecGeneric) {
513 0 : simulcast_idx = codec_info->codecSpecific.generic.simulcast_idx;
514 : }
515 0 : if (codec_info->codec_name) {
516 0 : stats_.encoder_implementation_name = codec_info->codec_name;
517 : }
518 : }
519 :
520 0 : if (simulcast_idx >= rtp_config_.ssrcs.size()) {
521 0 : LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
522 0 : << " >= " << rtp_config_.ssrcs.size() << ").";
523 0 : return;
524 : }
525 0 : uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx];
526 :
527 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
528 0 : if (!stats)
529 0 : return;
530 :
531 0 : stats->width = encoded_image._encodedWidth;
532 0 : stats->height = encoded_image._encodedHeight;
533 0 : update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
534 :
535 0 : uma_container_->key_frame_counter_.Add(encoded_image._frameType ==
536 0 : kVideoFrameKey);
537 0 : stats_.bw_limited_resolution =
538 0 : encoded_image.adapt_reason_.bw_resolutions_disabled > 0 ||
539 0 : quality_downscales_ > 0;
540 :
541 0 : if (quality_downscales_ != -1) {
542 0 : uma_container_->quality_limited_frame_counter_.Add(quality_downscales_ > 0);
543 0 : if (quality_downscales_ > 0)
544 0 : uma_container_->quality_downscales_counter_.Add(quality_downscales_);
545 : }
546 0 : if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) {
547 0 : bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0;
548 0 : uma_container_->bw_limited_frame_counter_.Add(bw_limited);
549 0 : if (bw_limited) {
550 0 : uma_container_->bw_resolutions_disabled_counter_.Add(
551 0 : encoded_image.adapt_reason_.bw_resolutions_disabled);
552 : }
553 : }
554 :
555 0 : if (encoded_image.qp_ != -1) {
556 0 : if (!stats_.qp_sum)
557 0 : stats_.qp_sum = rtc::Optional<uint64_t>(0);
558 0 : *stats_.qp_sum += encoded_image.qp_;
559 :
560 0 : if (codec_info) {
561 0 : if (codec_info->codecType == kVideoCodecVP8) {
562 0 : int spatial_idx = (rtp_config_.ssrcs.size() == 1)
563 0 : ? -1
564 0 : : static_cast<int>(simulcast_idx);
565 0 : uma_container_->qp_counters_[spatial_idx].vp8.Add(encoded_image.qp_);
566 0 : } else if (codec_info->codecType == kVideoCodecVP9) {
567 : int spatial_idx =
568 0 : (codec_info->codecSpecific.VP9.num_spatial_layers == 1)
569 0 : ? -1
570 0 : : codec_info->codecSpecific.VP9.spatial_idx;
571 0 : uma_container_->qp_counters_[spatial_idx].vp9.Add(encoded_image.qp_);
572 0 : } else if (codec_info->codecType == kVideoCodecH264) {
573 0 : int spatial_idx = -1;
574 0 : uma_container_->qp_counters_[spatial_idx].h264.Add(encoded_image.qp_);
575 : }
576 : }
577 : }
578 :
579 : // TODO(asapersson): This is incorrect if simulcast layers are encoded on
580 : // different threads and there is no guarantee that one frame of all layers
581 : // are encoded before the next start.
582 0 : if (last_sent_frame_timestamp_ > 0 &&
583 0 : encoded_image._timeStamp != last_sent_frame_timestamp_) {
584 0 : uma_container_->sent_fps_counter_.Add(1);
585 0 : uma_container_->sent_width_counter_.Add(
586 0 : uma_container_->max_sent_width_per_timestamp_);
587 0 : uma_container_->sent_height_counter_.Add(
588 0 : uma_container_->max_sent_height_per_timestamp_);
589 0 : uma_container_->max_sent_width_per_timestamp_ = 0;
590 0 : uma_container_->max_sent_height_per_timestamp_ = 0;
591 : }
592 0 : last_sent_frame_timestamp_ = encoded_image._timeStamp;
593 0 : uma_container_->max_sent_width_per_timestamp_ =
594 0 : std::max(uma_container_->max_sent_width_per_timestamp_,
595 0 : static_cast<int>(encoded_image._encodedWidth));
596 0 : uma_container_->max_sent_height_per_timestamp_ =
597 0 : std::max(uma_container_->max_sent_height_per_timestamp_,
598 0 : static_cast<int>(encoded_image._encodedHeight));
599 : }
600 :
601 0 : int SendStatisticsProxy::GetSendFrameRate() const {
602 0 : rtc::CritScope lock(&crit_);
603 0 : return stats_.encode_frame_rate;
604 : }
605 :
606 0 : void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
607 0 : rtc::CritScope lock(&crit_);
608 0 : uma_container_->input_frame_rate_tracker_.AddSamples(1);
609 0 : uma_container_->input_fps_counter_.Add(1);
610 0 : uma_container_->input_width_counter_.Add(width);
611 0 : uma_container_->input_height_counter_.Add(height);
612 0 : uma_container_->cpu_limited_frame_counter_.Add(stats_.cpu_limited_resolution);
613 0 : }
614 :
615 0 : void SendStatisticsProxy::SetResolutionRestrictionStats(
616 : bool scaling_enabled,
617 : bool cpu_restricted,
618 : int num_quality_downscales) {
619 0 : rtc::CritScope lock(&crit_);
620 0 : if (scaling_enabled) {
621 0 : quality_downscales_ = num_quality_downscales;
622 0 : stats_.bw_limited_resolution = quality_downscales_ > 0;
623 0 : stats_.cpu_limited_resolution = cpu_restricted;
624 : } else {
625 0 : stats_.bw_limited_resolution = false;
626 0 : stats_.cpu_limited_resolution = false;
627 0 : quality_downscales_ = -1;
628 : }
629 0 : }
630 :
631 0 : void SendStatisticsProxy::OnCpuRestrictedResolutionChanged(
632 : bool cpu_restricted_resolution) {
633 0 : rtc::CritScope lock(&crit_);
634 0 : stats_.cpu_limited_resolution = cpu_restricted_resolution;
635 0 : ++stats_.number_of_cpu_adapt_changes;
636 0 : }
637 :
638 0 : void SendStatisticsProxy::OnQualityRestrictedResolutionChanged(
639 : int num_quality_downscales) {
640 0 : rtc::CritScope lock(&crit_);
641 0 : quality_downscales_ = num_quality_downscales;
642 0 : stats_.bw_limited_resolution = quality_downscales_ > 0;
643 0 : }
644 :
645 0 : void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
646 : uint32_t ssrc,
647 : const RtcpPacketTypeCounter& packet_counter) {
648 0 : rtc::CritScope lock(&crit_);
649 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
650 0 : if (!stats)
651 0 : return;
652 :
653 0 : stats->rtcp_packet_type_counts = packet_counter;
654 0 : if (uma_container_->first_rtcp_stats_time_ms_ == -1)
655 0 : uma_container_->first_rtcp_stats_time_ms_ = clock_->TimeInMilliseconds();
656 : }
657 :
658 0 : void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
659 : uint32_t ssrc) {
660 0 : rtc::CritScope lock(&crit_);
661 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
662 0 : if (!stats)
663 0 : return;
664 :
665 0 : stats->rtcp_stats = statistics;
666 0 : uma_container_->report_block_stats_.Store(statistics, 0, ssrc);
667 : }
668 :
669 0 : void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {}
670 :
671 0 : void SendStatisticsProxy::DataCountersUpdated(
672 : const StreamDataCounters& counters,
673 : uint32_t ssrc) {
674 0 : rtc::CritScope lock(&crit_);
675 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
676 0 : RTC_DCHECK(stats) << "DataCountersUpdated reported for unknown ssrc: "
677 0 : << ssrc;
678 :
679 0 : if (stats->is_flexfec) {
680 : // The same counters are reported for both the media ssrc and flexfec ssrc.
681 : // Bitrate stats are summed for all SSRCs. Use fec stats from media update.
682 0 : return;
683 : }
684 :
685 0 : stats->rtp_stats = counters;
686 0 : if (uma_container_->first_rtp_stats_time_ms_ == -1)
687 0 : uma_container_->first_rtp_stats_time_ms_ = clock_->TimeInMilliseconds();
688 : }
689 :
690 0 : void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
691 : uint32_t retransmit_bitrate_bps,
692 : uint32_t ssrc) {
693 0 : rtc::CritScope lock(&crit_);
694 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
695 0 : if (!stats)
696 0 : return;
697 :
698 0 : stats->total_bitrate_bps = total_bitrate_bps;
699 0 : stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
700 : }
701 :
702 0 : void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
703 : uint32_t ssrc) {
704 0 : rtc::CritScope lock(&crit_);
705 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
706 0 : if (!stats)
707 0 : return;
708 :
709 0 : stats->frame_counts = frame_counts;
710 : }
711 :
712 0 : void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
713 : int max_delay_ms,
714 : uint32_t ssrc) {
715 0 : rtc::CritScope lock(&crit_);
716 0 : VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
717 0 : if (!stats)
718 0 : return;
719 0 : stats->avg_delay_ms = avg_delay_ms;
720 0 : stats->max_delay_ms = max_delay_ms;
721 :
722 0 : uma_container_->delay_counter_.Add(avg_delay_ms);
723 0 : uma_container_->max_delay_counter_.Add(max_delay_ms);
724 : }
725 :
726 0 : void SendStatisticsProxy::SampleCounter::Add(int sample) {
727 0 : sum += sample;
728 0 : ++num_samples;
729 0 : }
730 :
731 0 : int SendStatisticsProxy::SampleCounter::Avg(
732 : int64_t min_required_samples) const {
733 0 : if (num_samples < min_required_samples || num_samples == 0)
734 0 : return -1;
735 0 : return static_cast<int>((sum + (num_samples / 2)) / num_samples);
736 : }
737 :
738 0 : void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
739 0 : if (sample)
740 0 : ++sum;
741 0 : ++num_samples;
742 0 : }
743 :
744 0 : void SendStatisticsProxy::BoolSampleCounter::Add(bool sample, int64_t count) {
745 0 : if (sample)
746 0 : sum += count;
747 0 : num_samples += count;
748 0 : }
749 0 : int SendStatisticsProxy::BoolSampleCounter::Percent(
750 : int64_t min_required_samples) const {
751 0 : return Fraction(min_required_samples, 100.0f);
752 : }
753 :
754 0 : int SendStatisticsProxy::BoolSampleCounter::Permille(
755 : int64_t min_required_samples) const {
756 0 : return Fraction(min_required_samples, 1000.0f);
757 : }
758 :
759 0 : int SendStatisticsProxy::BoolSampleCounter::Fraction(
760 : int64_t min_required_samples,
761 : float multiplier) const {
762 0 : if (num_samples < min_required_samples || num_samples == 0)
763 0 : return -1;
764 0 : return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
765 : }
766 : } // namespace webrtc
|