Add callbacks for receive channel RTCP statistics.

This allows a listener to receive new statistics as it is generated - avoiding the need to poll. This also makes handling stats from multiple RTP streams more tractable.
The change is primarily targeted at the new video engine API.

TEST=Unit test in ReceiveStatisticsTest. Integration tests to follow as call tests when fully wired up.

BUG=2235
R=henrika@webrtc.org, pbos@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5323 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
index 707adaa..d034b49 100644
--- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
+++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
@@ -23,24 +23,9 @@
 
 class StreamStatistician {
  public:
-  struct Statistics {
-    Statistics()
-        : fraction_lost(0),
-          cumulative_lost(0),
-          extended_max_sequence_number(0),
-          jitter(0),
-          max_jitter(0) {}
-
-    uint8_t fraction_lost;
-    uint32_t cumulative_lost;
-    uint32_t extended_max_sequence_number;
-    uint32_t jitter;
-    uint32_t max_jitter;
-  };
-
   virtual ~StreamStatistician();
 
-  virtual bool GetStatistics(Statistics* statistics, bool reset) = 0;
+  virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) = 0;
   virtual void GetDataCounters(uint32_t* bytes_received,
                                uint32_t* packets_received) const = 0;
   virtual uint32_t BitrateReceived() const = 0;
@@ -78,6 +63,10 @@
 
   // Sets the max reordering threshold in number of packets.
   virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
+
+  // Called on new RTCP stats creation.
+  virtual void RegisterRtcpStatisticsCallback(
+      RtcpStatisticsCallback* callback) = 0;
 };
 
 class NullReceiveStatistics : public ReceiveStatistics {
@@ -89,6 +78,8 @@
   virtual int32_t TimeUntilNextProcess() OVERRIDE;
   virtual int32_t Process() OVERRIDE;
   virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
+  virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
+      OVERRIDE;
 };
 
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
index 3ed44b8..2b085c3 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -24,14 +24,15 @@
 
 StreamStatistician::~StreamStatistician() {}
 
-StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock)
+StreamStatisticianImpl::StreamStatisticianImpl(
+    Clock* clock,
+    RtcpStatisticsCallback* rtcp_callback)
     : clock_(clock),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       incoming_bitrate_(clock, NULL),
       ssrc_(0),
       max_reordering_threshold_(kDefaultMaxReorderingThreshold),
       jitter_q4_(0),
-      jitter_max_q4_(0),
       cumulative_loss_(0),
       jitter_q4_transmission_time_offset_(0),
       last_receive_time_ms_(0),
@@ -50,7 +51,8 @@
       last_report_inorder_packets_(0),
       last_report_old_packets_(0),
       last_report_seq_max_(0),
-      last_reported_statistics_() {}
+      last_reported_statistics_(),
+      rtcp_callback_(rtcp_callback) {}
 
 void StreamStatisticianImpl::ResetStatistics() {
   CriticalSectionScoped cs(crit_sect_.get());
@@ -59,7 +61,6 @@
   last_report_seq_max_ = 0;
   memset(&last_reported_statistics_, 0, sizeof(last_reported_statistics_));
   jitter_q4_ = 0;
-  jitter_max_q4_ = 0;
   cumulative_loss_ = 0;
   jitter_q4_transmission_time_offset_ = 0;
   received_seq_wraps_ = 0;
@@ -173,7 +174,8 @@
   max_reordering_threshold_ = max_reordering_threshold;
 }
 
-bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
+bool StreamStatisticianImpl::GetStatistics(RtcpStatistics* statistics,
+                                           bool reset) {
   CriticalSectionScoped cs(crit_sect_.get());
   if (received_seq_first_ == 0 && received_byte_count_ == 0) {
     // We have not received anything.
@@ -235,16 +237,11 @@
 
   // We need a counter for cumulative loss too.
   cumulative_loss_ += missing;
-
-  if (jitter_q4_ > jitter_max_q4_) {
-    jitter_max_q4_ = jitter_q4_;
-  }
   statistics->cumulative_lost = cumulative_loss_;
   statistics->extended_max_sequence_number = (received_seq_wraps_ << 16) +
       received_seq_max_;
   // Note: internal jitter value is in Q4 and needs to be scaled by 1/16.
   statistics->jitter = jitter_q4_ >> 4;
-  statistics->max_jitter = jitter_max_q4_ >> 4;
   if (reset) {
     // Store this report.
     last_reported_statistics_ = *statistics;
@@ -254,6 +251,8 @@
     last_report_old_packets_ = received_retransmitted_packets_;
     last_report_seq_max_ = received_seq_max_;
   }
+
+  rtcp_callback_->StatisticsUpdated(last_reported_statistics_, ssrc_);
   return true;
 }
 
@@ -349,7 +348,8 @@
 ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock)
     : clock_(clock),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
-      last_rate_update_ms_(0) {}
+      last_rate_update_ms_(0),
+      rtcp_stats_callback_(NULL) {}
 
 ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
   while (!statisticians_.empty()) {
@@ -365,7 +365,7 @@
   if (it == statisticians_.end()) {
     std::pair<StatisticianImplMap::iterator, uint32_t> insert_result =
         statisticians_.insert(std::make_pair(
-            header.ssrc,  new StreamStatisticianImpl(clock_)));
+            header.ssrc, new StreamStatisticianImpl(clock_, this)));
     it = insert_result.first;
   }
   statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet);
@@ -433,6 +433,21 @@
   return std::max(kStatisticsProcessIntervalMs - time_since_last_update, 0);
 }
 
+void ReceiveStatisticsImpl::RegisterRtcpStatisticsCallback(
+    RtcpStatisticsCallback* callback) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  if (callback != NULL)
+    assert(rtcp_stats_callback_ == NULL);
+  rtcp_stats_callback_ = callback;
+}
+
+void ReceiveStatisticsImpl::StatisticsUpdated(const RtcpStatistics& statistics,
+                                              uint32_t ssrc) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  if (rtcp_stats_callback_) {
+    rtcp_stats_callback_->StatisticsUpdated(statistics, ssrc);
+  }
+}
 
 void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
                                            size_t bytes,
@@ -454,4 +469,7 @@
 
 int32_t NullReceiveStatistics::Process() { return 0; }
 
+void NullReceiveStatistics::RegisterRtcpStatisticsCallback(
+    RtcpStatisticsCallback* callback) {}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
index 0af074c..bb6de33 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -25,11 +25,10 @@
 
 class StreamStatisticianImpl : public StreamStatistician {
  public:
-  explicit StreamStatisticianImpl(Clock* clock);
-
+  StreamStatisticianImpl(Clock* clock, RtcpStatisticsCallback* rtcp_callback);
   virtual ~StreamStatisticianImpl() {}
 
-  virtual bool GetStatistics(Statistics* statistics, bool reset) OVERRIDE;
+  virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) OVERRIDE;
   virtual void GetDataCounters(uint32_t* bytes_received,
                                uint32_t* packets_received) const OVERRIDE;
   virtual uint32_t BitrateReceived() const OVERRIDE;
@@ -55,7 +54,6 @@
 
   // Stats on received RTP packets.
   uint32_t jitter_q4_;
-  uint32_t jitter_max_q4_;
   uint32_t cumulative_loss_;
   uint32_t jitter_q4_transmission_time_offset_;
 
@@ -79,10 +77,13 @@
   uint32_t last_report_inorder_packets_;
   uint32_t last_report_old_packets_;
   uint16_t last_report_seq_max_;
-  Statistics last_reported_statistics_;
+  RtcpStatistics last_reported_statistics_;
+
+  RtcpStatisticsCallback* const rtcp_callback_;
 };
 
-class ReceiveStatisticsImpl : public ReceiveStatistics {
+class ReceiveStatisticsImpl : public ReceiveStatistics,
+                              public RtcpStatisticsCallback {
  public:
   explicit ReceiveStatisticsImpl(Clock* clock);
 
@@ -101,6 +102,12 @@
 
   void ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc);
 
+  virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
+      OVERRIDE;
+
+  virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+                                 uint32_t ssrc) OVERRIDE;
+
  private:
   typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
 
@@ -108,6 +115,8 @@
   scoped_ptr<CriticalSectionWrapper> crit_sect_;
   int64_t last_rate_update_ms_;
   StatisticianImplMap statisticians_;
+
+  RtcpStatisticsCallback* rtcp_stats_callback_;
 };
 }  // namespace webrtc
 #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVE_STATISTICS_IMPL_H_
diff --git a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
index be8f2fc..6969bbc 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -131,4 +131,87 @@
   EXPECT_EQ(200u, bytes_received);
   EXPECT_EQ(2u, packets_received);
 }
+
+TEST_F(ReceiveStatisticsTest, Callbacks) {
+  class TestCallback : public RtcpStatisticsCallback {
+   public:
+    TestCallback()
+        : RtcpStatisticsCallback(), num_calls_(0), ssrc_(0), stats_() {}
+    virtual ~TestCallback() {}
+
+    virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+                                   uint32_t ssrc) {
+      ssrc_ = ssrc;
+      stats_ = statistics;
+      ++num_calls_;
+    }
+
+    uint32_t num_calls_;
+    uint32_t ssrc_;
+    RtcpStatistics stats_;
+  } callback;
+
+  receive_statistics_->RegisterRtcpStatisticsCallback(&callback);
+
+  // Add some arbitrary data, with loss and jitter.
+  header1_.sequenceNumber = 1;
+  clock_.AdvanceTimeMilliseconds(7);
+  header1_.timestamp += 3;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  header1_.sequenceNumber += 2;
+  clock_.AdvanceTimeMilliseconds(9);
+  header1_.timestamp += 9;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  --header1_.sequenceNumber;
+  clock_.AdvanceTimeMilliseconds(13);
+  header1_.timestamp += 47;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
+  header1_.sequenceNumber += 3;
+  clock_.AdvanceTimeMilliseconds(11);
+  header1_.timestamp += 17;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  ++header1_.sequenceNumber;
+
+  EXPECT_EQ(0u, callback.num_calls_);
+
+  // Call GetStatistics, simulating a timed rtcp sender thread.
+  RtcpStatistics statistics;
+  receive_statistics_->GetStatistician(kSsrc1)
+      ->GetStatistics(&statistics, true);
+
+  EXPECT_EQ(1u, callback.num_calls_);
+  EXPECT_EQ(callback.ssrc_, kSsrc1);
+  EXPECT_EQ(statistics.cumulative_lost, callback.stats_.cumulative_lost);
+  EXPECT_EQ(statistics.extended_max_sequence_number,
+            callback.stats_.extended_max_sequence_number);
+  EXPECT_EQ(statistics.fraction_lost, callback.stats_.fraction_lost);
+  EXPECT_EQ(statistics.jitter, callback.stats_.jitter);
+
+  receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
+
+  // Add some more data.
+  header1_.sequenceNumber = 1;
+  clock_.AdvanceTimeMilliseconds(7);
+  header1_.timestamp += 3;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  header1_.sequenceNumber += 2;
+  clock_.AdvanceTimeMilliseconds(9);
+  header1_.timestamp += 9;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  --header1_.sequenceNumber;
+  clock_.AdvanceTimeMilliseconds(13);
+  header1_.timestamp += 47;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
+  header1_.sequenceNumber += 3;
+  clock_.AdvanceTimeMilliseconds(11);
+  header1_.timestamp += 17;
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
+  ++header1_.sequenceNumber;
+
+  receive_statistics_->GetStatistician(kSsrc1)
+      ->GetStatistics(&statistics, true);
+
+  // Should not have been called after deregister.
+  EXPECT_EQ(1u, callback.num_calls_);
+}
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc
index a407a42..8cac996 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -2065,7 +2065,7 @@
                                RTCPReportBlock* report_block,
                                uint32_t* ntp_secs, uint32_t* ntp_frac) {
   // Do we have receive statistics to send?
-  StreamStatistician::Statistics stats;
+  RtcpStatistics stats;
   if (!statistician->GetStatistics(&stats, true))
     return false;
   report_block->fractionLost = stats.fraction_lost;
diff --git a/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc b/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc
index 3df06a2..1e71518 100644
--- a/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc
+++ b/webrtc/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc
@@ -336,7 +336,7 @@
 
   StreamStatistician *statistician =
       receive_statistics2_->GetStatistician(reportBlockReceived.sourceSSRC);
-  StreamStatistician::Statistics stats;
+  RtcpStatistics stats;
   EXPECT_TRUE(statistician->GetStatistics(&stats, true));
   EXPECT_EQ(0, stats.fraction_lost);
   EXPECT_EQ((uint32_t)0, stats.cumulative_lost);
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index b03b76a..42259e3 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -348,7 +348,8 @@
       stats_.cumulative_lost = cumulative_lost;
       stats_.extended_max_sequence_number = extended_max_sequence_number;
     }
-    virtual bool GetStatistics(Statistics* statistics, bool reset) OVERRIDE {
+    virtual bool GetStatistics(RtcpStatistics* statistics,
+                               bool reset) OVERRIDE {
       *statistics = stats_;
       return true;
     }
@@ -367,7 +368,8 @@
     virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE {
       return true;
     }
-    Statistics stats_;
+
+    RtcpStatistics stats_;
   };
 
   scoped_ptr<LossyStatistician> lossy_stats_;
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index 2305ea7..5473177 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -345,6 +345,8 @@
       }
       rtp_rtcp->SetSendingStatus(rtp_rtcp_->Sending());
       rtp_rtcp->SetSendingMediaStatus(rtp_rtcp_->SendingMedia());
+      rtp_rtcp->RegisterSendChannelRtcpStatisticsCallback(
+          rtp_rtcp_->GetSendChannelRtcpStatisticsCallback());
       simulcast_rtp_rtcp_.push_back(rtp_rtcp);
     }
     // Remove last in list if we have too many.
@@ -1297,7 +1299,7 @@
   uint32_t remote_ssrc = vie_receiver_.GetRemoteSsrc();
   StreamStatistician* statistician =
       vie_receiver_.GetReceiveStatistics()->GetStatistician(remote_ssrc);
-  StreamStatistician::Statistics receive_stats;
+  RtcpStatistics receive_stats;
   if (!statistician || !statistician->GetStatistics(
       &receive_stats, rtp_rtcp_->RTCP() == kRtcpOff)) {
     WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
@@ -1319,6 +1321,17 @@
   return 0;
 }
 
+void ViEChannel::RegisterReceiveChannelRtcpStatisticsCallback(
+    RtcpStatisticsCallback* callback) {
+  WEBRTC_TRACE(kTraceInfo,
+               kTraceVideo,
+               ViEId(engine_id_, channel_id_),
+               "%s",
+               __FUNCTION__);
+  vie_receiver_.GetReceiveStatistics()->RegisterRtcpStatisticsCallback(
+      callback);
+}
+
 int32_t ViEChannel::GetRtpStatistics(uint32_t* bytes_sent,
                                      uint32_t* packets_sent,
                                      uint32_t* bytes_received,
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 33bf7bf..dd602ee 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -185,6 +185,10 @@
                                     uint32_t* jitter_samples,
                                     int32_t* rtt_ms);
 
+  // Called on generation of RTCP stats
+  void RegisterReceiveChannelRtcpStatisticsCallback(
+      RtcpStatisticsCallback* callback);
+
   // Gets sent/received packets statistics.
   int32_t GetRtpStatistics(uint32_t* bytes_sent,
                            uint32_t* packets_sent,
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index 2bd47be..d9f2211 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -1168,15 +1168,35 @@
 }
 
 int ViERTP_RTCPImpl::RegisterReceiveChannelRtcpStatisticsCallback(
-    int channel, RtcpStatisticsCallback* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    RtcpStatisticsCallback* callback) {
+  WEBRTC_TRACE(kTraceApiCall,
+               kTraceVideo,
+               ViEId(shared_data_->instance_id(), video_channel),
+               "%s(channel: %d)",
+               __FUNCTION__,
+               video_channel);
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterReceiveChannelRtcpStatisticsCallback(callback);
+  return 0;
 }
 
 int ViERTP_RTCPImpl::DeregisterReceiveChannelRtcpStatisticsCallback(
-    int channel, RtcpStatisticsCallback* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    RtcpStatisticsCallback* callback) {
+  WEBRTC_TRACE(kTraceApiCall,
+               kTraceVideo,
+               ViEId(shared_data_->instance_id(), video_channel),
+               "%s(channel: %d)",
+               __FUNCTION__,
+               video_channel);
+  ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
+  ViEChannel* vie_channel = cs.Channel(video_channel);
+  assert(vie_channel != NULL);
+  vie_channel->RegisterReceiveChannelRtcpStatisticsCallback(NULL);
+  return 0;
 }
 
 int ViERTP_RTCPImpl::RegisterSendChannelRtpStatisticsCallback(
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 1eb55af..2724f52 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -38,6 +38,54 @@
 namespace webrtc {
 namespace voe {
 
+// Extend the default RTCP statistics struct with max_jitter, defined as the
+// maximum jitter value seen in an RTCP report block.
+struct ChannelStatistics : public RtcpStatistics {
+  ChannelStatistics() : rtcp(), max_jitter(0) {}
+
+  RtcpStatistics rtcp;
+  uint32_t max_jitter;
+};
+
+// Statistics callback, called at each generation of a new RTCP report block.
+class StatisticsProxy : public RtcpStatisticsCallback {
+ public:
+  StatisticsProxy(uint32_t ssrc)
+   : stats_lock_(CriticalSectionWrapper::CreateCriticalSection()),
+     ssrc_(ssrc) {}
+  virtual ~StatisticsProxy() {}
+
+  virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+                                 uint32_t ssrc) OVERRIDE {
+    if (ssrc != ssrc_)
+      return;
+
+    CriticalSectionScoped cs(stats_lock_.get());
+    stats_.rtcp = statistics;
+    if (statistics.jitter > stats_.max_jitter) {
+      stats_.max_jitter = statistics.jitter;
+    }
+  }
+
+  void ResetStatistics() {
+    CriticalSectionScoped cs(stats_lock_.get());
+    stats_ = ChannelStatistics();
+  }
+
+  ChannelStatistics GetStats() {
+    CriticalSectionScoped cs(stats_lock_.get());
+    return stats_;
+  }
+
+ private:
+  // StatisticsUpdated calls are triggered from threads in the RTP module,
+  // while GetStats calls can be triggered from the public voice engine API,
+  // hence synchronization is needed.
+  scoped_ptr<CriticalSectionWrapper> stats_lock_;
+  const uint32_t ssrc_;
+  ChannelStatistics stats_;
+};
+
 int32_t
 Channel::SendData(FrameType frameType,
                   uint8_t   payloadType,
@@ -361,6 +409,7 @@
   if (statistician) {
     statistician->ResetStatistics();
   }
+  statistics_proxy_->ResetStatistics();
 }
 
 void
@@ -883,6 +932,7 @@
     _rtpDumpOut(*RtpDump::CreateRtpDump()),
     _outputAudioLevel(),
     _externalTransport(false),
+    _audioLevel_dBov(0),
     _inputFilePlayerPtr(NULL),
     _outputFilePlayerPtr(NULL),
     _outputFileRecorderPtr(NULL),
@@ -909,6 +959,7 @@
     jitter_buffer_playout_timestamp_(0),
     playout_timestamp_rtp_(0),
     playout_timestamp_rtcp_(0),
+    playout_delay_ms_(0),
     _numberOfDiscardedPackets(0),
     send_sequence_number_(0),
     _engineStatisticsPtr(NULL),
@@ -984,10 +1035,15 @@
     configuration.receive_statistics = rtp_receive_statistics_.get();
 
     _rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
+
+    statistics_proxy_.reset(new StatisticsProxy(_rtpRtcpModule->SSRC()));
+    rtp_receive_statistics_->RegisterRtcpStatisticsCallback(
+        statistics_proxy_.get());
 }
 
 Channel::~Channel()
 {
+    rtp_receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
                  "Channel::~Channel() - dtor");
 
@@ -3863,23 +3919,25 @@
 {
     // The jitter statistics is updated for each received RTP packet and is
     // based on received packets.
-    StreamStatistician::Statistics statistics;
-    StreamStatistician* statistician =
-        rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
-    if (!statistician || !statistician->GetStatistics(
-        &statistics, _rtpRtcpModule->RTCP() == kRtcpOff)) {
-      _engineStatisticsPtr->SetLastError(
-          VE_CANNOT_RETRIEVE_RTP_STAT, kTraceWarning,
-          "GetRTPStatistics() failed to read RTP statistics from the "
-          "RTP/RTCP module");
+    if (_rtpRtcpModule->RTCP() == kRtcpOff) {
+      // If RTCP is off, there is no timed thread in the RTCP module regularly
+      // generating new stats, trigger the update manually here instead.
+      StreamStatistician* statistician =
+          rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
+      if (statistician) {
+        // Don't use returned statistics, use data from proxy instead so that
+        // max jitter can be fetched atomically.
+        RtcpStatistics s;
+        statistician->GetStatistics(&s, true);
+      }
     }
 
+    ChannelStatistics stats = statistics_proxy_->GetStats();
     const int32_t playoutFrequency = audio_coding_->PlayoutFrequency();
-    if (playoutFrequency > 0)
-    {
-        // Scale RTP statistics given the current playout frequency
-        maxJitterMs = statistics.max_jitter / (playoutFrequency / 1000);
-        averageJitterMs = statistics.jitter / (playoutFrequency / 1000);
+    if (playoutFrequency > 0) {
+      // Scale RTP statistics given the current playout frequency
+      maxJitterMs = stats.max_jitter / (playoutFrequency / 1000);
+      averageJitterMs = stats.rtcp.jitter / (playoutFrequency / 1000);
     }
 
     discardedPackets = _numberOfDiscardedPackets;
@@ -3959,7 +4017,7 @@
 
     // The jitter statistics is updated for each received RTP packet and is
     // based on received packets.
-    StreamStatistician::Statistics statistics;
+    RtcpStatistics statistics;
     StreamStatistician* statistician =
         rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
     if (!statistician || !statistician->GetStatistics(
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index f8b04fd..48d50c2 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -59,6 +59,7 @@
 namespace voe {
 
 class Statistics;
+class StatisticsProxy;
 class TransmitMixer;
 class OutputMixer;
 
@@ -455,6 +456,7 @@
     scoped_ptr<RtpHeaderParser> rtp_header_parser_;
     scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
     scoped_ptr<ReceiveStatistics> rtp_receive_statistics_;
+    scoped_ptr<StatisticsProxy> statistics_proxy_;
     scoped_ptr<RtpReceiver> rtp_receiver_;
     TelephoneEventHandler* telephone_event_handler_;
     scoped_ptr<RtpRtcp> _rtpRtcpModule;