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/receive_statistics_proxy.h"
12
13#include <cmath>
14
15#include "webrtc/base/checks.h"
16#include "webrtc/modules/video_coding/include/video_codec_interface.h"
17#include "webrtc/system_wrappers/include/clock.h"
18#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
19#include "webrtc/system_wrappers/include/metrics.h"
20
21namespace webrtc {
22
23ReceiveStatisticsProxy::ReceiveStatisticsProxy(uint32_t ssrc, Clock* clock)
24    : clock_(clock),
25      // 1000ms window, scale 1000 for ms to s.
26      decode_fps_estimator_(1000, 1000),
27      renders_fps_estimator_(1000, 1000),
28      render_fps_tracker_(100u, 10u),
29      render_pixel_tracker_(100u, 10u) {
30  stats_.ssrc = ssrc;
31}
32
33ReceiveStatisticsProxy::~ReceiveStatisticsProxy() {
34  UpdateHistograms();
35}
36
37void ReceiveStatisticsProxy::UpdateHistograms() {
38  int fraction_lost = report_block_stats_.FractionLostInPercent();
39  if (fraction_lost != -1) {
40    RTC_HISTOGRAM_PERCENTAGE_SPARSE("WebRTC.Video.ReceivedPacketsLostInPercent",
41                                    fraction_lost);
42  }
43  const int kMinRequiredSamples = 200;
44  int samples = static_cast<int>(render_fps_tracker_.TotalSampleCount());
45  if (samples > kMinRequiredSamples) {
46    RTC_HISTOGRAM_COUNTS_SPARSE_100("WebRTC.Video.RenderFramesPerSecond",
47        round(render_fps_tracker_.ComputeTotalRate()));
48    RTC_HISTOGRAM_COUNTS_SPARSE_100000("WebRTC.Video.RenderSqrtPixelsPerSecond",
49        round(render_pixel_tracker_.ComputeTotalRate()));
50  }
51  int width = render_width_counter_.Avg(kMinRequiredSamples);
52  int height = render_height_counter_.Avg(kMinRequiredSamples);
53  if (width != -1) {
54    RTC_HISTOGRAM_COUNTS_SPARSE_10000("WebRTC.Video.ReceivedWidthInPixels",
55                                      width);
56    RTC_HISTOGRAM_COUNTS_SPARSE_10000("WebRTC.Video.ReceivedHeightInPixels",
57                                      height);
58  }
59  int qp = qp_counters_.vp8.Avg(kMinRequiredSamples);
60  if (qp != -1)
61    RTC_HISTOGRAM_COUNTS_SPARSE_200("WebRTC.Video.Decoded.Vp8.Qp", qp);
62
63  // TODO(asapersson): DecoderTiming() is call periodically (each 1000ms) and
64  // not per frame. Change decode time to include every frame.
65  const int kMinRequiredDecodeSamples = 5;
66  int decode_ms = decode_time_counter_.Avg(kMinRequiredDecodeSamples);
67  if (decode_ms != -1)
68    RTC_HISTOGRAM_COUNTS_SPARSE_1000("WebRTC.Video.DecodeTimeInMs", decode_ms);
69
70  int delay_ms = delay_counter_.Avg(kMinRequiredDecodeSamples);
71  if (delay_ms != -1)
72    RTC_HISTOGRAM_COUNTS_SPARSE_10000("WebRTC.Video.OnewayDelayInMs", delay_ms);
73}
74
75VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
76  rtc::CritScope lock(&crit_);
77  return stats_;
78}
79
80void ReceiveStatisticsProxy::OnIncomingPayloadType(int payload_type) {
81  rtc::CritScope lock(&crit_);
82  stats_.current_payload_type = payload_type;
83}
84
85void ReceiveStatisticsProxy::OnDecoderImplementationName(
86    const char* implementation_name) {
87  rtc::CritScope lock(&crit_);
88  stats_.decoder_implementation_name = implementation_name;
89}
90void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate,
91                                            unsigned int bitrate_bps) {
92  rtc::CritScope lock(&crit_);
93  stats_.network_frame_rate = framerate;
94  stats_.total_bitrate_bps = bitrate_bps;
95}
96
97void ReceiveStatisticsProxy::OnDecoderTiming(int decode_ms,
98                                             int max_decode_ms,
99                                             int current_delay_ms,
100                                             int target_delay_ms,
101                                             int jitter_buffer_ms,
102                                             int min_playout_delay_ms,
103                                             int render_delay_ms,
104                                             int64_t rtt_ms) {
105  rtc::CritScope lock(&crit_);
106  stats_.decode_ms = decode_ms;
107  stats_.max_decode_ms = max_decode_ms;
108  stats_.current_delay_ms = current_delay_ms;
109  stats_.target_delay_ms = target_delay_ms;
110  stats_.jitter_buffer_ms = jitter_buffer_ms;
111  stats_.min_playout_delay_ms = min_playout_delay_ms;
112  stats_.render_delay_ms = render_delay_ms;
113  decode_time_counter_.Add(decode_ms);
114  // Network delay (rtt/2) + target_delay_ms (jitter delay + decode time +
115  // render delay).
116  delay_counter_.Add(target_delay_ms + rtt_ms / 2);
117}
118
119void ReceiveStatisticsProxy::RtcpPacketTypesCounterUpdated(
120    uint32_t ssrc,
121    const RtcpPacketTypeCounter& packet_counter) {
122  rtc::CritScope lock(&crit_);
123  if (stats_.ssrc != ssrc)
124    return;
125  stats_.rtcp_packet_type_counts = packet_counter;
126}
127
128void ReceiveStatisticsProxy::StatisticsUpdated(
129    const webrtc::RtcpStatistics& statistics,
130    uint32_t ssrc) {
131  rtc::CritScope lock(&crit_);
132  // TODO(pbos): Handle both local and remote ssrcs here and RTC_DCHECK that we
133  // receive stats from one of them.
134  if (stats_.ssrc != ssrc)
135    return;
136  stats_.rtcp_stats = statistics;
137  report_block_stats_.Store(statistics, ssrc, 0);
138}
139
140void ReceiveStatisticsProxy::CNameChanged(const char* cname, uint32_t ssrc) {
141  rtc::CritScope lock(&crit_);
142  // TODO(pbos): Handle both local and remote ssrcs here and RTC_DCHECK that we
143  // receive stats from one of them.
144  if (stats_.ssrc != ssrc)
145    return;
146  stats_.c_name = cname;
147}
148
149void ReceiveStatisticsProxy::DataCountersUpdated(
150    const webrtc::StreamDataCounters& counters,
151    uint32_t ssrc) {
152  rtc::CritScope lock(&crit_);
153  if (stats_.ssrc != ssrc)
154    return;
155  stats_.rtp_stats = counters;
156}
157
158void ReceiveStatisticsProxy::OnDecodedFrame() {
159  uint64_t now = clock_->TimeInMilliseconds();
160
161  rtc::CritScope lock(&crit_);
162  decode_fps_estimator_.Update(1, now);
163  stats_.decode_frame_rate = decode_fps_estimator_.Rate(now);
164}
165
166void ReceiveStatisticsProxy::OnRenderedFrame(int width, int height) {
167  RTC_DCHECK_GT(width, 0);
168  RTC_DCHECK_GT(height, 0);
169  uint64_t now = clock_->TimeInMilliseconds();
170
171  rtc::CritScope lock(&crit_);
172  renders_fps_estimator_.Update(1, now);
173  stats_.render_frame_rate = renders_fps_estimator_.Rate(now);
174  render_width_counter_.Add(width);
175  render_height_counter_.Add(height);
176  render_fps_tracker_.AddSamples(1);
177  render_pixel_tracker_.AddSamples(sqrt(width * height));
178}
179
180void ReceiveStatisticsProxy::OnReceiveRatesUpdated(uint32_t bitRate,
181                                                   uint32_t frameRate) {
182}
183
184void ReceiveStatisticsProxy::OnFrameCountsUpdated(
185    const FrameCounts& frame_counts) {
186  rtc::CritScope lock(&crit_);
187  stats_.frame_counts = frame_counts;
188}
189
190void ReceiveStatisticsProxy::OnDiscardedPacketsUpdated(int discarded_packets) {
191  rtc::CritScope lock(&crit_);
192  stats_.discarded_packets = discarded_packets;
193}
194
195void ReceiveStatisticsProxy::OnPreDecode(
196    const EncodedImage& encoded_image,
197    const CodecSpecificInfo* codec_specific_info) {
198  if (codec_specific_info == nullptr || encoded_image.qp_ == -1) {
199    return;
200  }
201  if (codec_specific_info->codecType == kVideoCodecVP8) {
202    qp_counters_.vp8.Add(encoded_image.qp_);
203  }
204}
205
206void ReceiveStatisticsProxy::SampleCounter::Add(int sample) {
207  sum += sample;
208  ++num_samples;
209}
210
211int ReceiveStatisticsProxy::SampleCounter::Avg(int min_required_samples) const {
212  if (num_samples < min_required_samples || num_samples == 0)
213    return -1;
214  return sum / num_samples;
215}
216
217}  // namespace webrtc
218