Change LastProcessedRtt (used in the rtp/rtcp module) to return the average RTT (instead of max RTT) to get a smooth estimate of the nack interval.

R=mflodman@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/27349004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7863 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/call_stats.cc b/webrtc/video_engine/call_stats.cc
index 05385c3..ccc97fa 100644
--- a/webrtc/video_engine/call_stats.cc
+++ b/webrtc/video_engine/call_stats.cc
@@ -17,11 +17,57 @@
 #include "webrtc/system_wrappers/interface/tick_util.h"
 
 namespace webrtc {
-
+namespace {
 // A rtt report is considered valid for this long.
 const int kRttTimeoutMs = 1500;
 // Time interval for updating the observers.
 const int kUpdateIntervalMs = 1000;
+// Weight factor to apply to the average rtt.
+const float kWeightFactor = 0.3f;
+
+void RemoveOldReports(int64_t now, std::list<CallStats::RttTime>* reports) {
+  while (!reports->empty() &&
+         (now - reports->front().time) > kRttTimeoutMs) {
+    reports->pop_front();
+  }
+}
+
+uint32_t GetMaxRttMs(std::list<CallStats::RttTime>* reports) {
+  uint32_t max_rtt_ms = 0;
+  for (std::list<CallStats::RttTime>::const_iterator it = reports->begin();
+       it != reports->end(); ++it) {
+    max_rtt_ms = std::max(it->rtt, max_rtt_ms);
+  }
+  return max_rtt_ms;
+}
+
+uint32_t GetAvgRttMs(std::list<CallStats::RttTime>* reports) {
+  if (reports->empty()) {
+    return 0;
+  }
+  uint32_t sum = 0;
+  for (std::list<CallStats::RttTime>::const_iterator it = reports->begin();
+       it != reports->end(); ++it) {
+    sum += it->rtt;
+  }
+  return sum / reports->size();
+}
+
+void UpdateAvgRttMs(std::list<CallStats::RttTime>* reports, uint32_t* avg_rtt) {
+  uint32_t cur_rtt_ms = GetAvgRttMs(reports);
+  if (cur_rtt_ms == 0) {
+    // Reset.
+    *avg_rtt = 0;
+    return;
+  }
+  if (*avg_rtt == 0) {
+    // Initialize.
+    *avg_rtt = cur_rtt_ms;
+    return;
+  }
+  *avg_rtt = *avg_rtt * (1.0f - kWeightFactor) + cur_rtt_ms * kWeightFactor;
+}
+}  // namespace
 
 class RtcpObserver : public RtcpRttStats {
  public:
@@ -32,8 +78,9 @@
     owner_->OnRttUpdate(rtt);
   }
 
+  // Returns the average RTT.
   virtual uint32_t LastProcessedRtt() const {
-    return owner_->last_processed_rtt_ms();
+    return owner_->avg_rtt_ms();
   }
 
  private:
@@ -46,7 +93,8 @@
     : crit_(CriticalSectionWrapper::CreateCriticalSection()),
       rtcp_rtt_stats_(new RtcpObserver(this)),
       last_process_time_(TickTime::MillisecondTimestamp()),
-      last_processed_rtt_ms_(0) {
+      max_rtt_ms_(0),
+      avg_rtt_ms_(0) {
 }
 
 CallStats::~CallStats() {
@@ -60,39 +108,30 @@
 
 int32_t CallStats::Process() {
   CriticalSectionScoped cs(crit_.get());
-  if (TickTime::MillisecondTimestamp() < last_process_time_ + kUpdateIntervalMs)
+  int64_t now = TickTime::MillisecondTimestamp();
+  if (now < last_process_time_ + kUpdateIntervalMs)
     return 0;
 
-  // Remove invalid, as in too old, rtt values.
-  int64_t time_now = TickTime::MillisecondTimestamp();
-  while (!reports_.empty() && reports_.front().time + kRttTimeoutMs <
-         time_now) {
-    reports_.pop_front();
-  }
+  last_process_time_ = now;
 
-  // Find the max stored RTT.
-  uint32_t max_rtt = 0;
-  for (std::list<RttTime>::const_iterator it = reports_.begin();
-       it != reports_.end(); ++it) {
-    if (it->rtt > max_rtt)
-      max_rtt = it->rtt;
-  }
+  RemoveOldReports(now, &reports_);
+  max_rtt_ms_ = GetMaxRttMs(&reports_);
+  UpdateAvgRttMs(&reports_, &avg_rtt_ms_);
 
-  // If there is a valid rtt, update all observers.
-  if (max_rtt > 0) {
+  // If there is a valid rtt, update all observers with the max rtt.
+  // TODO(asapersson): Consider changing this to report the average rtt.
+  if (max_rtt_ms_ > 0) {
     for (std::list<CallStatsObserver*>::iterator it = observers_.begin();
          it != observers_.end(); ++it) {
-      (*it)->OnRttUpdate(max_rtt);
+      (*it)->OnRttUpdate(max_rtt_ms_);
     }
   }
-  last_processed_rtt_ms_ = max_rtt;
-  last_process_time_ = time_now;
   return 0;
 }
 
-uint32_t CallStats::last_processed_rtt_ms() const {
+uint32_t CallStats::avg_rtt_ms() const {
   CriticalSectionScoped cs(crit_.get());
-  return last_processed_rtt_ms_;
+  return avg_rtt_ms_;
 }
 
 RtcpRttStats* CallStats::rtcp_rtt_stats() const {
@@ -122,8 +161,7 @@
 
 void CallStats::OnRttUpdate(uint32_t rtt) {
   CriticalSectionScoped cs(crit_.get());
-  int64_t time_now = TickTime::MillisecondTimestamp();
-  reports_.push_back(RttTime(rtt, time_now));
+  reports_.push_back(RttTime(rtt, TickTime::MillisecondTimestamp()));
 }
 
 }  // namespace webrtc
diff --git a/webrtc/video_engine/call_stats.h b/webrtc/video_engine/call_stats.h
index 95f4fee..f516ebf 100644
--- a/webrtc/video_engine/call_stats.h
+++ b/webrtc/video_engine/call_stats.h
@@ -43,12 +43,6 @@
   void RegisterStatsObserver(CallStatsObserver* observer);
   void DeregisterStatsObserver(CallStatsObserver* observer);
 
- protected:
-  void OnRttUpdate(uint32_t rtt);
-
-  uint32_t last_processed_rtt_ms() const;
-
- private:
   // Helper struct keeping track of the time a rtt value is reported.
   struct RttTime {
     RttTime(uint32_t new_rtt, int64_t rtt_time)
@@ -57,6 +51,12 @@
     const int64_t time;
   };
 
+ protected:
+  void OnRttUpdate(uint32_t rtt);
+
+  uint32_t avg_rtt_ms() const;
+
+ private:
   // Protecting all members.
   scoped_ptr<CriticalSectionWrapper> crit_;
   // Observer receiving statistics updates.
@@ -64,7 +64,8 @@
   // The last time 'Process' resulted in statistic update.
   int64_t last_process_time_;
   // The last RTT in the statistics update (zero if there is no valid estimate).
-  uint32_t last_processed_rtt_ms_;
+  uint32_t max_rtt_ms_;
+  uint32_t avg_rtt_ms_;
 
   // All Rtt reports within valid time interval, oldest first.
   std::list<RttTime> reports_;
diff --git a/webrtc/video_engine/call_stats_unittest.cc b/webrtc/video_engine/call_stats_unittest.cc
index f504094..1196ada 100644
--- a/webrtc/video_engine/call_stats_unittest.cc
+++ b/webrtc/video_engine/call_stats_unittest.cc
@@ -101,42 +101,42 @@
 TEST_F(CallStatsTest, MultipleObservers) {
   MockStatsObserver stats_observer_1;
   call_stats_->RegisterStatsObserver(&stats_observer_1);
-  // Add the secondobserver twice, there should still be only one report to the
+  // Add the second observer twice, there should still be only one report to the
   // observer.
   MockStatsObserver stats_observer_2;
   call_stats_->RegisterStatsObserver(&stats_observer_2);
   call_stats_->RegisterStatsObserver(&stats_observer_2);
 
   RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
-  uint32_t rtt = 100;
-  rtcp_rtt_stats->OnRttUpdate(rtt);
+  const uint32_t kRtt = 100;
+  rtcp_rtt_stats->OnRttUpdate(kRtt);
 
   // Verify both observers are updated.
   TickTime::AdvanceFakeClock(1000);
-  EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt))
       .Times(1);
-  EXPECT_CALL(stats_observer_2, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt))
       .Times(1);
   call_stats_->Process();
 
   // Deregister the second observer and verify update is only sent to the first
   // observer.
   call_stats_->DeregisterStatsObserver(&stats_observer_2);
-  rtcp_rtt_stats->OnRttUpdate(rtt);
+  rtcp_rtt_stats->OnRttUpdate(kRtt);
   TickTime::AdvanceFakeClock(1000);
-  EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt))
       .Times(1);
-  EXPECT_CALL(stats_observer_2, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt))
       .Times(0);
   call_stats_->Process();
 
   // Deregister the first observer.
   call_stats_->DeregisterStatsObserver(&stats_observer_1);
-  rtcp_rtt_stats->OnRttUpdate(rtt);
+  rtcp_rtt_stats->OnRttUpdate(kRtt);
   TickTime::AdvanceFakeClock(1000);
-  EXPECT_CALL(stats_observer_1, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt))
       .Times(0);
-  EXPECT_CALL(stats_observer_2, OnRttUpdate(rtt))
+  EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt))
       .Times(0);
   call_stats_->Process();
 }
@@ -151,17 +151,17 @@
   TickTime::AdvanceFakeClock(1000);
 
   // Set a first value and verify the callback is triggered.
-  const uint32_t first_rtt = 100;
-  rtcp_rtt_stats->OnRttUpdate(first_rtt);
-  EXPECT_CALL(stats_observer, OnRttUpdate(first_rtt))
+  const uint32_t kFirstRtt = 100;
+  rtcp_rtt_stats->OnRttUpdate(kFirstRtt);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kFirstRtt))
       .Times(1);
   call_stats_->Process();
 
   // Increase rtt and verify the new value is reported.
   TickTime::AdvanceFakeClock(1000);
-  const uint32_t high_rtt = first_rtt + 20;
-  rtcp_rtt_stats->OnRttUpdate(high_rtt);
-  EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
+  const uint32_t kHighRtt = kFirstRtt + 20;
+  rtcp_rtt_stats->OnRttUpdate(kHighRtt);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kHighRtt))
       .Times(1);
   call_stats_->Process();
 
@@ -169,20 +169,50 @@
   // rtt invalid. Report a lower rtt and verify the old/high value still is sent
   // in the callback.
   TickTime::AdvanceFakeClock(1000);
-  const uint32_t low_rtt = first_rtt - 20;
-  rtcp_rtt_stats->OnRttUpdate(low_rtt);
-  EXPECT_CALL(stats_observer, OnRttUpdate(high_rtt))
+  const uint32_t kLowRtt = kFirstRtt - 20;
+  rtcp_rtt_stats->OnRttUpdate(kLowRtt);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kHighRtt))
       .Times(1);
   call_stats_->Process();
 
   // Advance time to make the high report invalid, the lower rtt should now be
   // in the callback.
   TickTime::AdvanceFakeClock(1000);
-  EXPECT_CALL(stats_observer, OnRttUpdate(low_rtt))
+  EXPECT_CALL(stats_observer, OnRttUpdate(kLowRtt))
       .Times(1);
   call_stats_->Process();
 
   call_stats_->DeregisterStatsObserver(&stats_observer);
 }
 
+TEST_F(CallStatsTest, LastProcessedRtt) {
+  MockStatsObserver stats_observer;
+  call_stats_->RegisterStatsObserver(&stats_observer);
+  RtcpRttStats* rtcp_rtt_stats = call_stats_->rtcp_rtt_stats();
+  TickTime::AdvanceFakeClock(1000);
+
+  // Set a first values and verify that LastProcessedRtt initially returns the
+  // average rtt.
+  const uint32_t kRttLow = 10;
+  const uint32_t kRttHigh = 30;
+  const uint32_t kAvgRtt = 20;
+  rtcp_rtt_stats->OnRttUpdate(kRttLow);
+  rtcp_rtt_stats->OnRttUpdate(kRttHigh);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kRttHigh))
+      .Times(1);
+  call_stats_->Process();
+  EXPECT_EQ(kAvgRtt, rtcp_rtt_stats->LastProcessedRtt());
+
+  // Update values and verify LastProcessedRtt.
+  TickTime::AdvanceFakeClock(1000);
+  rtcp_rtt_stats->OnRttUpdate(kRttLow);
+  rtcp_rtt_stats->OnRttUpdate(kRttHigh);
+  EXPECT_CALL(stats_observer, OnRttUpdate(kRttHigh))
+      .Times(1);
+  call_stats_->Process();
+  EXPECT_EQ(kAvgRtt, rtcp_rtt_stats->LastProcessedRtt());
+
+  call_stats_->DeregisterStatsObserver(&stats_observer);
+}
+
 }  // namespace webrtc