1 /*
2 * Copyright (c) 2012 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 "video/call_stats.h"
12
13 #include <algorithm>
14
15 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/constructormagic.h"
18 #include "system_wrappers/include/metrics.h"
19
20 namespace webrtc {
21 namespace {
22 // Time interval for updating the observers.
23 const int64_t kUpdateIntervalMs = 1000;
24 // Weight factor to apply to the average rtt.
25 const float kWeightFactor = 0.3f;
26
RemoveOldReports(int64_t now,std::list<CallStats::RttTime> * reports)27 void RemoveOldReports(int64_t now, std::list<CallStats::RttTime>* reports) {
28 // A rtt report is considered valid for this long.
29 const int64_t kRttTimeoutMs = 1500;
30 while (!reports->empty() &&
31 (now - reports->front().time) > kRttTimeoutMs) {
32 reports->pop_front();
33 }
34 }
35
GetMaxRttMs(std::list<CallStats::RttTime> * reports)36 int64_t GetMaxRttMs(std::list<CallStats::RttTime>* reports) {
37 if (reports->empty())
38 return -1;
39 int64_t max_rtt_ms = 0;
40 for (const CallStats::RttTime& rtt_time : *reports)
41 max_rtt_ms = std::max(rtt_time.rtt, max_rtt_ms);
42 return max_rtt_ms;
43 }
44
GetAvgRttMs(std::list<CallStats::RttTime> * reports)45 int64_t GetAvgRttMs(std::list<CallStats::RttTime>* reports) {
46 if (reports->empty()) {
47 return -1;
48 }
49 int64_t sum = 0;
50 for (std::list<CallStats::RttTime>::const_iterator it = reports->begin();
51 it != reports->end(); ++it) {
52 sum += it->rtt;
53 }
54 return sum / reports->size();
55 }
56
UpdateAvgRttMs(std::list<CallStats::RttTime> * reports,int64_t * avg_rtt)57 void UpdateAvgRttMs(std::list<CallStats::RttTime>* reports, int64_t* avg_rtt) {
58 int64_t cur_rtt_ms = GetAvgRttMs(reports);
59 if (cur_rtt_ms == -1) {
60 // Reset.
61 *avg_rtt = -1;
62 return;
63 }
64 if (*avg_rtt == -1) {
65 // Initialize.
66 *avg_rtt = cur_rtt_ms;
67 return;
68 }
69 *avg_rtt = *avg_rtt * (1.0f - kWeightFactor) + cur_rtt_ms * kWeightFactor;
70 }
71 } // namespace
72
73 class RtcpObserver : public RtcpRttStats {
74 public:
RtcpObserver(CallStats * owner)75 explicit RtcpObserver(CallStats* owner) : owner_(owner) {}
~RtcpObserver()76 virtual ~RtcpObserver() {}
77
OnRttUpdate(int64_t rtt)78 virtual void OnRttUpdate(int64_t rtt) {
79 owner_->OnRttUpdate(rtt);
80 }
81
82 // Returns the average RTT.
LastProcessedRtt() const83 virtual int64_t LastProcessedRtt() const {
84 return owner_->avg_rtt_ms();
85 }
86
87 private:
88 CallStats* owner_;
89
90 RTC_DISALLOW_COPY_AND_ASSIGN(RtcpObserver);
91 };
92
CallStats(Clock * clock)93 CallStats::CallStats(Clock* clock)
94 : clock_(clock),
95 rtcp_rtt_stats_(new RtcpObserver(this)),
96 last_process_time_(clock_->TimeInMilliseconds()),
97 max_rtt_ms_(-1),
98 avg_rtt_ms_(-1),
99 sum_avg_rtt_ms_(0),
100 num_avg_rtt_(0),
101 time_of_first_rtt_ms_(-1) {}
102
~CallStats()103 CallStats::~CallStats() {
104 RTC_DCHECK(observers_.empty());
105 UpdateHistograms();
106 }
107
TimeUntilNextProcess()108 int64_t CallStats::TimeUntilNextProcess() {
109 return last_process_time_ + kUpdateIntervalMs - clock_->TimeInMilliseconds();
110 }
111
Process()112 void CallStats::Process() {
113 rtc::CritScope cs(&crit_);
114 int64_t now = clock_->TimeInMilliseconds();
115 if (now < last_process_time_ + kUpdateIntervalMs)
116 return;
117
118 last_process_time_ = now;
119
120 RemoveOldReports(now, &reports_);
121 max_rtt_ms_ = GetMaxRttMs(&reports_);
122 UpdateAvgRttMs(&reports_, &avg_rtt_ms_);
123
124 // If there is a valid rtt, update all observers with the max rtt.
125 if (max_rtt_ms_ >= 0) {
126 RTC_DCHECK_GE(avg_rtt_ms_, 0);
127 for (std::list<CallStatsObserver*>::iterator it = observers_.begin();
128 it != observers_.end(); ++it) {
129 (*it)->OnRttUpdate(avg_rtt_ms_, max_rtt_ms_);
130 }
131 // Sum for Histogram of average RTT reported over the entire call.
132 sum_avg_rtt_ms_ += avg_rtt_ms_;
133 ++num_avg_rtt_;
134 }
135 }
136
avg_rtt_ms() const137 int64_t CallStats::avg_rtt_ms() const {
138 rtc::CritScope cs(&crit_);
139 return avg_rtt_ms_;
140 }
141
rtcp_rtt_stats() const142 RtcpRttStats* CallStats::rtcp_rtt_stats() const {
143 return rtcp_rtt_stats_.get();
144 }
145
RegisterStatsObserver(CallStatsObserver * observer)146 void CallStats::RegisterStatsObserver(CallStatsObserver* observer) {
147 rtc::CritScope cs(&crit_);
148 for (std::list<CallStatsObserver*>::iterator it = observers_.begin();
149 it != observers_.end(); ++it) {
150 if (*it == observer)
151 return;
152 }
153 observers_.push_back(observer);
154 }
155
DeregisterStatsObserver(CallStatsObserver * observer)156 void CallStats::DeregisterStatsObserver(CallStatsObserver* observer) {
157 rtc::CritScope cs(&crit_);
158 for (std::list<CallStatsObserver*>::iterator it = observers_.begin();
159 it != observers_.end(); ++it) {
160 if (*it == observer) {
161 observers_.erase(it);
162 return;
163 }
164 }
165 }
166
OnRttUpdate(int64_t rtt)167 void CallStats::OnRttUpdate(int64_t rtt) {
168 rtc::CritScope cs(&crit_);
169 int64_t now_ms = clock_->TimeInMilliseconds();
170 reports_.push_back(RttTime(rtt, now_ms));
171 if (time_of_first_rtt_ms_ == -1)
172 time_of_first_rtt_ms_ = now_ms;
173 }
174
UpdateHistograms()175 void CallStats::UpdateHistograms() {
176 rtc::CritScope cs(&crit_);
177 if (time_of_first_rtt_ms_ == -1 || num_avg_rtt_ < 1)
178 return;
179
180 int64_t elapsed_sec =
181 (clock_->TimeInMilliseconds() - time_of_first_rtt_ms_) / 1000;
182 if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
183 int64_t avg_rtt_ms = (sum_avg_rtt_ms_ + num_avg_rtt_ / 2) / num_avg_rtt_;
184 RTC_HISTOGRAM_COUNTS_10000(
185 "WebRTC.Video.AverageRoundTripTimeInMilliseconds", avg_rtt_ms);
186 }
187 }
188
189 } // namespace webrtc
190