send_statistics_proxy.cc revision 415d2cd7454d93b3727fce9147090a24e4c3ccba
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 <map>
15
16#include "webrtc/base/checks.h"
17
18#include "webrtc/base/logging.h"
19#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
20#include "webrtc/system_wrappers/interface/metrics.h"
21
22namespace webrtc {
23
24const int SendStatisticsProxy::kStatsTimeoutMs = 5000;
25
26SendStatisticsProxy::SendStatisticsProxy(Clock* clock,
27                                         const VideoSendStream::Config& config)
28    : clock_(clock),
29      config_(config),
30      input_frame_rate_tracker_(100u, 10u),
31      sent_frame_rate_tracker_(100u, 10u),
32      last_sent_frame_timestamp_(0),
33      max_sent_width_per_timestamp_(0),
34      max_sent_height_per_timestamp_(0) {
35}
36
37SendStatisticsProxy::~SendStatisticsProxy() {
38  UpdateHistograms();
39}
40
41void SendStatisticsProxy::UpdateHistograms() {
42  int input_fps =
43      static_cast<int>(input_frame_rate_tracker_.ComputeTotalRate());
44  if (input_fps > 0)
45    RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.InputFramesPerSecond", input_fps);
46  int sent_fps =
47      static_cast<int>(sent_frame_rate_tracker_.ComputeTotalRate());
48  if (sent_fps > 0)
49    RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.SentFramesPerSecond", sent_fps);
50
51  const int kMinRequiredSamples = 200;
52  int in_width = input_width_counter_.Avg(kMinRequiredSamples);
53  int in_height = input_height_counter_.Avg(kMinRequiredSamples);
54  if (in_width != -1) {
55    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InputWidthInPixels", in_width);
56    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.InputHeightInPixels", in_height);
57  }
58  int sent_width = sent_width_counter_.Avg(kMinRequiredSamples);
59  int sent_height = sent_height_counter_.Avg(kMinRequiredSamples);
60  if (sent_width != -1) {
61    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.SentWidthInPixels", sent_width);
62    RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.SentHeightInPixels", sent_height);
63  }
64  int encode_ms = encode_time_counter_.Avg(kMinRequiredSamples);
65  if (encode_ms != -1)
66    RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.EncodeTimeInMs", encode_ms);
67
68  int key_frames_permille = key_frame_counter_.Permille(kMinRequiredSamples);
69  if (key_frames_permille != -1) {
70    RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesSentInPermille",
71        key_frames_permille);
72  }
73  int quality_limited =
74      quality_limited_frame_counter_.Percent(kMinRequiredSamples);
75  if (quality_limited != -1) {
76    RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.QualityLimitedResolutionInPercent",
77                             quality_limited);
78  }
79  int downscales = quality_downscales_counter_.Avg(kMinRequiredSamples);
80  if (downscales != -1) {
81    RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.QualityLimitedResolutionDownscales",
82                              downscales, 20);
83  }
84  int bw_limited = bw_limited_frame_counter_.Percent(kMinRequiredSamples);
85  if (bw_limited != -1) {
86    RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BandwidthLimitedResolutionInPercent",
87        bw_limited);
88  }
89  int num_disabled = bw_resolutions_disabled_counter_.Avg(kMinRequiredSamples);
90  if (num_disabled != -1) {
91    RTC_HISTOGRAM_ENUMERATION(
92        "WebRTC.Video.BandwidthLimitedResolutionsDisabled", num_disabled, 10);
93  }
94}
95
96void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) {
97  rtc::CritScope lock(&crit_);
98  stats_.encode_frame_rate = framerate;
99  stats_.media_bitrate_bps = bitrate;
100}
101
102void SendStatisticsProxy::CpuOveruseMetricsUpdated(
103    const CpuOveruseMetrics& metrics) {
104  rtc::CritScope lock(&crit_);
105  // TODO(asapersson): Change to use OnEncodedFrame() for avg_encode_time_ms.
106  stats_.avg_encode_time_ms = metrics.avg_encode_time_ms;
107  stats_.encode_usage_percent = metrics.encode_usage_percent;
108}
109
110void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
111  rtc::CritScope lock(&crit_);
112  stats_.suspended = is_suspended;
113}
114
115VideoSendStream::Stats SendStatisticsProxy::GetStats() {
116  rtc::CritScope lock(&crit_);
117  PurgeOldStats();
118  stats_.input_frame_rate =
119      static_cast<int>(input_frame_rate_tracker_.ComputeRate());
120  return stats_;
121}
122
123void SendStatisticsProxy::PurgeOldStats() {
124  int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
125  for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
126           stats_.substreams.begin();
127       it != stats_.substreams.end(); ++it) {
128    uint32_t ssrc = it->first;
129    if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
130      it->second.width = 0;
131      it->second.height = 0;
132    }
133  }
134}
135
136VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
137    uint32_t ssrc) {
138  std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
139      stats_.substreams.find(ssrc);
140  if (it != stats_.substreams.end())
141    return &it->second;
142
143  if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) ==
144          config_.rtp.ssrcs.end() &&
145      std::find(config_.rtp.rtx.ssrcs.begin(),
146                config_.rtp.rtx.ssrcs.end(),
147                ssrc) == config_.rtp.rtx.ssrcs.end()) {
148    return nullptr;
149  }
150
151  return &stats_.substreams[ssrc];  // Insert new entry and return ptr.
152}
153
154void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
155  rtc::CritScope lock(&crit_);
156  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
157  if (stats == nullptr)
158    return;
159
160  stats->total_bitrate_bps = 0;
161  stats->retransmit_bitrate_bps = 0;
162  stats->height = 0;
163  stats->width = 0;
164}
165
166void SendStatisticsProxy::OnSetRates(uint32_t bitrate_bps, int framerate) {
167  rtc::CritScope lock(&crit_);
168  stats_.target_media_bitrate_bps = bitrate_bps;
169}
170
171void SendStatisticsProxy::OnSendEncodedImage(
172    const EncodedImage& encoded_image,
173    const RTPVideoHeader* rtp_video_header) {
174  size_t simulcast_idx =
175      rtp_video_header != nullptr ? rtp_video_header->simulcastIdx : 0;
176  if (simulcast_idx >= config_.rtp.ssrcs.size()) {
177    LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
178                  << " >= " << config_.rtp.ssrcs.size() << ").";
179    return;
180  }
181  uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx];
182
183  rtc::CritScope lock(&crit_);
184  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
185  if (stats == nullptr)
186    return;
187
188  stats->width = encoded_image._encodedWidth;
189  stats->height = encoded_image._encodedHeight;
190  update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
191
192  key_frame_counter_.Add(encoded_image._frameType == kVideoFrameKey);
193
194  if (encoded_image.adapt_reason_.quality_resolution_downscales != -1) {
195    bool downscaled =
196        encoded_image.adapt_reason_.quality_resolution_downscales > 0;
197    quality_limited_frame_counter_.Add(downscaled);
198    if (downscaled) {
199      quality_downscales_counter_.Add(
200          encoded_image.adapt_reason_.quality_resolution_downscales);
201    }
202  }
203  if (encoded_image.adapt_reason_.bw_resolutions_disabled != -1) {
204    bool bw_limited = encoded_image.adapt_reason_.bw_resolutions_disabled > 0;
205    bw_limited_frame_counter_.Add(bw_limited);
206    if (bw_limited) {
207      bw_resolutions_disabled_counter_.Add(
208         encoded_image.adapt_reason_.bw_resolutions_disabled);
209    }
210  }
211
212  // TODO(asapersson): This is incorrect if simulcast layers are encoded on
213  // different threads and there is no guarantee that one frame of all layers
214  // are encoded before the next start.
215  if (last_sent_frame_timestamp_ > 0 &&
216      encoded_image._timeStamp != last_sent_frame_timestamp_) {
217    sent_frame_rate_tracker_.AddSamples(1);
218    sent_width_counter_.Add(max_sent_width_per_timestamp_);
219    sent_height_counter_.Add(max_sent_height_per_timestamp_);
220    max_sent_width_per_timestamp_ = 0;
221    max_sent_height_per_timestamp_ = 0;
222  }
223  last_sent_frame_timestamp_ = encoded_image._timeStamp;
224  max_sent_width_per_timestamp_ =
225      std::max(max_sent_width_per_timestamp_,
226               static_cast<int>(encoded_image._encodedWidth));
227  max_sent_height_per_timestamp_ =
228      std::max(max_sent_height_per_timestamp_,
229               static_cast<int>(encoded_image._encodedHeight));
230}
231
232void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
233  rtc::CritScope lock(&crit_);
234  input_frame_rate_tracker_.AddSamples(1);
235  input_width_counter_.Add(width);
236  input_height_counter_.Add(height);
237}
238
239void SendStatisticsProxy::OnEncodedFrame(int encode_time_ms) {
240  rtc::CritScope lock(&crit_);
241  encode_time_counter_.Add(encode_time_ms);
242}
243
244void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
245    uint32_t ssrc,
246    const RtcpPacketTypeCounter& packet_counter) {
247  rtc::CritScope lock(&crit_);
248  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
249  if (stats == nullptr)
250    return;
251
252  stats->rtcp_packet_type_counts = packet_counter;
253}
254
255void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
256                                            uint32_t ssrc) {
257  rtc::CritScope lock(&crit_);
258  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
259  if (stats == nullptr)
260    return;
261
262  stats->rtcp_stats = statistics;
263}
264
265void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
266}
267
268void SendStatisticsProxy::DataCountersUpdated(
269    const StreamDataCounters& counters,
270    uint32_t ssrc) {
271  rtc::CritScope lock(&crit_);
272  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
273  RTC_DCHECK(stats != nullptr)
274      << "DataCountersUpdated reported for unknown ssrc: " << ssrc;
275
276  stats->rtp_stats = counters;
277}
278
279void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
280                                 const BitrateStatistics& retransmit_stats,
281                                 uint32_t ssrc) {
282  rtc::CritScope lock(&crit_);
283  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
284  if (stats == nullptr)
285    return;
286
287  stats->total_bitrate_bps = total_stats.bitrate_bps;
288  stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps;
289}
290
291void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
292                                            uint32_t ssrc) {
293  rtc::CritScope lock(&crit_);
294  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
295  if (stats == nullptr)
296    return;
297
298  stats->frame_counts = frame_counts;
299}
300
301void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
302                                               int max_delay_ms,
303                                               uint32_t ssrc) {
304  rtc::CritScope lock(&crit_);
305  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
306  if (stats == nullptr)
307    return;
308  stats->avg_delay_ms = avg_delay_ms;
309  stats->max_delay_ms = max_delay_ms;
310}
311
312void SendStatisticsProxy::SampleCounter::Add(int sample) {
313  sum += sample;
314  ++num_samples;
315}
316
317int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const {
318  if (num_samples < min_required_samples || num_samples == 0)
319    return -1;
320  return (sum + (num_samples / 2)) / num_samples;
321}
322
323void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
324  if (sample)
325    ++sum;
326  ++num_samples;
327}
328
329int SendStatisticsProxy::BoolSampleCounter::Percent(
330    int min_required_samples) const {
331  return Fraction(min_required_samples, 100.0f);
332}
333
334int SendStatisticsProxy::BoolSampleCounter::Permille(
335    int min_required_samples) const {
336  return Fraction(min_required_samples, 1000.0f);
337}
338
339int SendStatisticsProxy::BoolSampleCounter::Fraction(
340    int min_required_samples, float multiplier) const {
341  if (num_samples < min_required_samples || num_samples == 0)
342    return -1;
343  return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
344}
345
346}  // namespace webrtc
347