send_statistics_proxy.cc revision 5d0379da2cbdcce6f8494209c7ab559cd6de076e
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/system_wrappers/interface/critical_section_wrapper.h"
19#include "webrtc/system_wrappers/interface/logging.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}
74
75void SendStatisticsProxy::OnOutgoingRate(uint32_t framerate, uint32_t bitrate) {
76  rtc::CritScope lock(&crit_);
77  stats_.encode_frame_rate = framerate;
78  stats_.media_bitrate_bps = bitrate;
79}
80
81void SendStatisticsProxy::CpuOveruseMetricsUpdated(
82    const CpuOveruseMetrics& metrics) {
83  rtc::CritScope lock(&crit_);
84  // TODO(asapersson): Change to use OnEncodedFrame() for avg_encode_time_ms.
85  stats_.avg_encode_time_ms = metrics.avg_encode_time_ms;
86  stats_.encode_usage_percent = metrics.encode_usage_percent;
87}
88
89void SendStatisticsProxy::OnSuspendChange(bool is_suspended) {
90  rtc::CritScope lock(&crit_);
91  stats_.suspended = is_suspended;
92}
93
94VideoSendStream::Stats SendStatisticsProxy::GetStats() {
95  rtc::CritScope lock(&crit_);
96  PurgeOldStats();
97  stats_.input_frame_rate =
98      static_cast<int>(input_frame_rate_tracker_.ComputeRate());
99  return stats_;
100}
101
102void SendStatisticsProxy::PurgeOldStats() {
103  int64_t old_stats_ms = clock_->TimeInMilliseconds() - kStatsTimeoutMs;
104  for (std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
105           stats_.substreams.begin();
106       it != stats_.substreams.end(); ++it) {
107    uint32_t ssrc = it->first;
108    if (update_times_[ssrc].resolution_update_ms <= old_stats_ms) {
109      it->second.width = 0;
110      it->second.height = 0;
111    }
112  }
113}
114
115VideoSendStream::StreamStats* SendStatisticsProxy::GetStatsEntry(
116    uint32_t ssrc) {
117  std::map<uint32_t, VideoSendStream::StreamStats>::iterator it =
118      stats_.substreams.find(ssrc);
119  if (it != stats_.substreams.end())
120    return &it->second;
121
122  if (std::find(config_.rtp.ssrcs.begin(), config_.rtp.ssrcs.end(), ssrc) ==
123          config_.rtp.ssrcs.end() &&
124      std::find(config_.rtp.rtx.ssrcs.begin(),
125                config_.rtp.rtx.ssrcs.end(),
126                ssrc) == config_.rtp.rtx.ssrcs.end()) {
127    return nullptr;
128  }
129
130  return &stats_.substreams[ssrc];  // Insert new entry and return ptr.
131}
132
133void SendStatisticsProxy::OnInactiveSsrc(uint32_t ssrc) {
134  rtc::CritScope lock(&crit_);
135  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
136  if (stats == nullptr)
137    return;
138
139  stats->total_bitrate_bps = 0;
140  stats->retransmit_bitrate_bps = 0;
141  stats->height = 0;
142  stats->width = 0;
143}
144
145void SendStatisticsProxy::OnSetRates(uint32_t bitrate_bps, int framerate) {
146  rtc::CritScope lock(&crit_);
147  stats_.target_media_bitrate_bps = bitrate_bps;
148}
149
150void SendStatisticsProxy::OnSendEncodedImage(
151    const EncodedImage& encoded_image,
152    const RTPVideoHeader* rtp_video_header) {
153  size_t simulcast_idx =
154      rtp_video_header != nullptr ? rtp_video_header->simulcastIdx : 0;
155  if (simulcast_idx >= config_.rtp.ssrcs.size()) {
156    LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx
157                  << " >= " << config_.rtp.ssrcs.size() << ").";
158    return;
159  }
160  uint32_t ssrc = config_.rtp.ssrcs[simulcast_idx];
161
162  rtc::CritScope lock(&crit_);
163  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
164  if (stats == nullptr)
165    return;
166
167  stats->width = encoded_image._encodedWidth;
168  stats->height = encoded_image._encodedHeight;
169  update_times_[ssrc].resolution_update_ms = clock_->TimeInMilliseconds();
170
171  key_frame_counter_.Add(encoded_image._frameType == kKeyFrame);
172
173  // TODO(asapersson): This is incorrect if simulcast layers are encoded on
174  // different threads and there is no guarantee that one frame of all layers
175  // are encoded before the next start.
176  if (last_sent_frame_timestamp_ > 0 &&
177      encoded_image._timeStamp != last_sent_frame_timestamp_) {
178    sent_frame_rate_tracker_.AddSamples(1);
179    sent_width_counter_.Add(max_sent_width_per_timestamp_);
180    sent_height_counter_.Add(max_sent_height_per_timestamp_);
181    max_sent_width_per_timestamp_ = 0;
182    max_sent_height_per_timestamp_ = 0;
183  }
184  last_sent_frame_timestamp_ = encoded_image._timeStamp;
185  max_sent_width_per_timestamp_ =
186      std::max(max_sent_width_per_timestamp_,
187               static_cast<int>(encoded_image._encodedWidth));
188  max_sent_height_per_timestamp_ =
189      std::max(max_sent_height_per_timestamp_,
190               static_cast<int>(encoded_image._encodedHeight));
191}
192
193void SendStatisticsProxy::OnIncomingFrame(int width, int height) {
194  rtc::CritScope lock(&crit_);
195  input_frame_rate_tracker_.AddSamples(1);
196  input_width_counter_.Add(width);
197  input_height_counter_.Add(height);
198}
199
200void SendStatisticsProxy::OnEncodedFrame(int encode_time_ms) {
201  rtc::CritScope lock(&crit_);
202  encode_time_counter_.Add(encode_time_ms);
203}
204
205void SendStatisticsProxy::RtcpPacketTypesCounterUpdated(
206    uint32_t ssrc,
207    const RtcpPacketTypeCounter& packet_counter) {
208  rtc::CritScope lock(&crit_);
209  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
210  if (stats == nullptr)
211    return;
212
213  stats->rtcp_packet_type_counts = packet_counter;
214}
215
216void SendStatisticsProxy::StatisticsUpdated(const RtcpStatistics& statistics,
217                                            uint32_t ssrc) {
218  rtc::CritScope lock(&crit_);
219  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
220  if (stats == nullptr)
221    return;
222
223  stats->rtcp_stats = statistics;
224}
225
226void SendStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
227}
228
229void SendStatisticsProxy::DataCountersUpdated(
230    const StreamDataCounters& counters,
231    uint32_t ssrc) {
232  rtc::CritScope lock(&crit_);
233  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
234  RTC_DCHECK(stats != nullptr)
235      << "DataCountersUpdated reported for unknown ssrc: " << ssrc;
236
237  stats->rtp_stats = counters;
238}
239
240void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
241                                 const BitrateStatistics& retransmit_stats,
242                                 uint32_t ssrc) {
243  rtc::CritScope lock(&crit_);
244  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
245  if (stats == nullptr)
246    return;
247
248  stats->total_bitrate_bps = total_stats.bitrate_bps;
249  stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps;
250}
251
252void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,
253                                            uint32_t ssrc) {
254  rtc::CritScope lock(&crit_);
255  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
256  if (stats == nullptr)
257    return;
258
259  stats->frame_counts = frame_counts;
260}
261
262void SendStatisticsProxy::SendSideDelayUpdated(int avg_delay_ms,
263                                               int max_delay_ms,
264                                               uint32_t ssrc) {
265  rtc::CritScope lock(&crit_);
266  VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
267  if (stats == nullptr)
268    return;
269  stats->avg_delay_ms = avg_delay_ms;
270  stats->max_delay_ms = max_delay_ms;
271}
272
273void SendStatisticsProxy::SampleCounter::Add(int sample) {
274  sum += sample;
275  ++num_samples;
276}
277
278int SendStatisticsProxy::SampleCounter::Avg(int min_required_samples) const {
279  if (num_samples < min_required_samples || num_samples == 0)
280    return -1;
281  return sum / num_samples;
282}
283
284void SendStatisticsProxy::BoolSampleCounter::Add(bool sample) {
285  if (sample)
286    ++sum;
287  ++num_samples;
288}
289
290int SendStatisticsProxy::BoolSampleCounter::Percent(
291    int min_required_samples) const {
292  return Fraction(min_required_samples, 100.0f);
293}
294
295int SendStatisticsProxy::BoolSampleCounter::Permille(
296    int min_required_samples) const {
297  return Fraction(min_required_samples, 1000.0f);
298}
299
300int SendStatisticsProxy::BoolSampleCounter::Fraction(
301    int min_required_samples, float multiplier) const {
302  if (num_samples < min_required_samples || num_samples == 0)
303    return -1;
304  return static_cast<int>((sum * multiplier / num_samples) + 0.5f);
305}
306
307}  // namespace webrtc
308