1 /*
2  *  Copyright (c) 2020 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_stats2.h"
12 
13 #include <algorithm>
14 #include <memory>
15 #include <utility>
16 
17 #include "absl/algorithm/container.h"
18 #include "modules/utility/include/process_thread.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/location.h"
21 #include "rtc_base/task_utils/to_queued_task.h"
22 #include "system_wrappers/include/metrics.h"
23 
24 namespace webrtc {
25 namespace internal {
26 namespace {
27 
RemoveOldReports(int64_t now,std::list<CallStats::RttTime> * reports)28 void RemoveOldReports(int64_t now, std::list<CallStats::RttTime>* reports) {
29   static constexpr const int64_t kRttTimeoutMs = 1500;
30   reports->remove_if(
31       [&now](CallStats::RttTime& r) { return now - r.time > kRttTimeoutMs; });
32 }
33 
GetMaxRttMs(const std::list<CallStats::RttTime> & reports)34 int64_t GetMaxRttMs(const std::list<CallStats::RttTime>& reports) {
35   int64_t max_rtt_ms = -1;
36   for (const CallStats::RttTime& rtt_time : reports)
37     max_rtt_ms = std::max(rtt_time.rtt, max_rtt_ms);
38   return max_rtt_ms;
39 }
40 
GetAvgRttMs(const std::list<CallStats::RttTime> & reports)41 int64_t GetAvgRttMs(const std::list<CallStats::RttTime>& reports) {
42   RTC_DCHECK(!reports.empty());
43   int64_t sum = 0;
44   for (std::list<CallStats::RttTime>::const_iterator it = reports.begin();
45        it != reports.end(); ++it) {
46     sum += it->rtt;
47   }
48   return sum / reports.size();
49 }
50 
GetNewAvgRttMs(const std::list<CallStats::RttTime> & reports,int64_t prev_avg_rtt)51 int64_t GetNewAvgRttMs(const std::list<CallStats::RttTime>& reports,
52                        int64_t prev_avg_rtt) {
53   if (reports.empty())
54     return -1;  // Reset (invalid average).
55 
56   int64_t cur_rtt_ms = GetAvgRttMs(reports);
57   if (prev_avg_rtt == -1)
58     return cur_rtt_ms;  // New initial average value.
59 
60   // Weight factor to apply to the average rtt.
61   // We weigh the old average at 70% against the new average (30%).
62   constexpr const float kWeightFactor = 0.3f;
63   return prev_avg_rtt * (1.0f - kWeightFactor) + cur_rtt_ms * kWeightFactor;
64 }
65 
66 }  // namespace
67 
68 constexpr TimeDelta CallStats::kUpdateInterval;
69 
CallStats(Clock * clock,TaskQueueBase * task_queue)70 CallStats::CallStats(Clock* clock, TaskQueueBase* task_queue)
71     : clock_(clock),
72       max_rtt_ms_(-1),
73       avg_rtt_ms_(-1),
74       sum_avg_rtt_ms_(0),
75       num_avg_rtt_(0),
76       time_of_first_rtt_ms_(-1),
77       task_queue_(task_queue) {
78   RTC_DCHECK(task_queue_);
79   process_thread_checker_.Detach();
80   repeating_task_ =
81       RepeatingTaskHandle::DelayedStart(task_queue_, kUpdateInterval, [this]() {
82         UpdateAndReport();
83         return kUpdateInterval;
84       });
85 }
86 
~CallStats()87 CallStats::~CallStats() {
88   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
89   RTC_DCHECK(observers_.empty());
90 
91   repeating_task_.Stop();
92 
93   UpdateHistograms();
94 }
95 
UpdateAndReport()96 void CallStats::UpdateAndReport() {
97   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
98 
99   RemoveOldReports(clock_->CurrentTime().ms(), &reports_);
100   max_rtt_ms_ = GetMaxRttMs(reports_);
101   avg_rtt_ms_ = GetNewAvgRttMs(reports_, avg_rtt_ms_);
102 
103   // If there is a valid rtt, update all observers with the max rtt.
104   if (max_rtt_ms_ >= 0) {
105     RTC_DCHECK_GE(avg_rtt_ms_, 0);
106     for (CallStatsObserver* observer : observers_)
107       observer->OnRttUpdate(avg_rtt_ms_, max_rtt_ms_);
108     // Sum for Histogram of average RTT reported over the entire call.
109     sum_avg_rtt_ms_ += avg_rtt_ms_;
110     ++num_avg_rtt_;
111   }
112 }
113 
RegisterStatsObserver(CallStatsObserver * observer)114 void CallStats::RegisterStatsObserver(CallStatsObserver* observer) {
115   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
116   if (!absl::c_linear_search(observers_, observer))
117     observers_.push_back(observer);
118 }
119 
DeregisterStatsObserver(CallStatsObserver * observer)120 void CallStats::DeregisterStatsObserver(CallStatsObserver* observer) {
121   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
122   observers_.remove(observer);
123 }
124 
LastProcessedRtt() const125 int64_t CallStats::LastProcessedRtt() const {
126   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
127   // No need for locking since we're on the construction thread.
128   return avg_rtt_ms_;
129 }
130 
OnRttUpdate(int64_t rtt)131 void CallStats::OnRttUpdate(int64_t rtt) {
132   // This callback may for some RtpRtcp module instances (video send stream) be
133   // invoked from a separate task queue, in other cases, we should already be
134   // on the correct TQ.
135   int64_t now_ms = clock_->TimeInMilliseconds();
136   auto update = [this, rtt, now_ms]() {
137     RTC_DCHECK_RUN_ON(&construction_thread_checker_);
138     reports_.push_back(RttTime(rtt, now_ms));
139     if (time_of_first_rtt_ms_ == -1)
140       time_of_first_rtt_ms_ = now_ms;
141     UpdateAndReport();
142   };
143 
144   if (task_queue_->IsCurrent()) {
145     update();
146   } else {
147     task_queue_->PostTask(ToQueuedTask(task_safety_, std::move(update)));
148   }
149 }
150 
UpdateHistograms()151 void CallStats::UpdateHistograms() {
152   RTC_DCHECK_RUN_ON(&construction_thread_checker_);
153 
154   if (time_of_first_rtt_ms_ == -1 || num_avg_rtt_ < 1)
155     return;
156 
157   int64_t elapsed_sec =
158       (clock_->TimeInMilliseconds() - time_of_first_rtt_ms_) / 1000;
159   if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
160     int64_t avg_rtt_ms = (sum_avg_rtt_ms_ + num_avg_rtt_ / 2) / num_avg_rtt_;
161     RTC_HISTOGRAM_COUNTS_10000(
162         "WebRTC.Video.AverageRoundTripTimeInMilliseconds", avg_rtt_ms);
163   }
164 }
165 
166 }  // namespace internal
167 }  // namespace webrtc
168