Add callbacks for receive channel RTP statistics

This allows a listener to receive new statistics (byte/packet counts, etc) 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=mflodman@webrtc.org, pbos@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5416 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 d034b49..6f2ea4f 100644
--- a/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
+++ b/webrtc/modules/rtp_rtcp/interface/receive_statistics.h
@@ -51,9 +51,13 @@
   static ReceiveStatistics* Create(Clock* clock);
 
   // Updates the receive statistics with this packet.
-  virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+  virtual void IncomingPacket(const RTPHeader& rtp_header,
+                              size_t bytes,
                               bool retransmitted) = 0;
 
+  // Increment counter for number of FEC packets received.
+  virtual void FecPacketReceived(uint32_t ssrc) = 0;
+
   // Returns a map of all statisticians which have seen an incoming packet
   // during the last two seconds.
   virtual StatisticianMap GetActiveStatisticians() const = 0;
@@ -67,12 +71,18 @@
   // Called on new RTCP stats creation.
   virtual void RegisterRtcpStatisticsCallback(
       RtcpStatisticsCallback* callback) = 0;
+
+  // Called on new RTP stats creation.
+  virtual void RegisterRtpStatisticsCallback(
+      StreamDataCountersCallback* callback) = 0;
 };
 
 class NullReceiveStatistics : public ReceiveStatistics {
  public:
-  virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+  virtual void IncomingPacket(const RTPHeader& rtp_header,
+                              size_t bytes,
                               bool retransmitted) OVERRIDE;
+  virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
   virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
   virtual int32_t TimeUntilNextProcess() OVERRIDE;
@@ -80,6 +90,8 @@
   virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
   virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
       OVERRIDE;
+  virtual void RegisterRtpStatisticsCallback(
+      StreamDataCountersCallback* 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 af0108b..cb00556 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -26,7 +26,8 @@
 
 StreamStatisticianImpl::StreamStatisticianImpl(
     Clock* clock,
-    RtcpStatisticsCallback* rtcp_callback)
+    RtcpStatisticsCallback* rtcp_callback,
+    StreamDataCountersCallback* rtp_callback)
     : clock_(clock),
       stream_lock_(CriticalSectionWrapper::CreateCriticalSection()),
       incoming_bitrate_(clock, NULL),
@@ -43,33 +44,26 @@
       received_seq_first_(0),
       received_seq_max_(0),
       received_seq_wraps_(0),
-      first_packet_(true),
       received_packet_overhead_(12),
-      received_byte_count_(0),
-      received_retransmitted_packets_(0),
-      received_inorder_packet_count_(0),
       last_report_inorder_packets_(0),
       last_report_old_packets_(0),
       last_report_seq_max_(0),
-      last_reported_statistics_(),
-      rtcp_callback_(rtcp_callback) {}
+      rtcp_callback_(rtcp_callback),
+      rtp_callback_(rtp_callback) {}
 
 void StreamStatisticianImpl::ResetStatistics() {
   CriticalSectionScoped cs(stream_lock_.get());
   last_report_inorder_packets_ = 0;
   last_report_old_packets_ = 0;
   last_report_seq_max_ = 0;
-  memset(&last_reported_statistics_, 0, sizeof(last_reported_statistics_));
+  last_reported_statistics_ = RtcpStatistics();
   jitter_q4_ = 0;
   cumulative_loss_ = 0;
   jitter_q4_transmission_time_offset_ = 0;
   received_seq_wraps_ = 0;
   received_seq_max_ = 0;
   received_seq_first_ = 0;
-  received_byte_count_ = 0;
-  received_retransmitted_packets_ = 0;
-  received_inorder_packet_count_ = 0;
-  first_packet_ = true;
+  receive_counters_ = StreamDataCounters();
 }
 
 void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
@@ -79,17 +73,17 @@
   bool in_order = InOrderPacketInternal(header.sequenceNumber);
   ssrc_ = header.ssrc;
   incoming_bitrate_.Update(bytes);
-  received_byte_count_ += bytes;
+  receive_counters_.bytes +=
+      bytes - (header.paddingLength + header.headerLength);
+  receive_counters_.header_bytes += header.headerLength;
+  receive_counters_.padding_bytes += header.paddingLength;
+  ++receive_counters_.packets;
+  if (!in_order && retransmitted) {
+    ++receive_counters_.retransmitted_packets;
+  }
 
-  if (first_packet_) {
-    first_packet_ = false;
-    // This is the first received report.
+  if (receive_counters_.packets == 1) {
     received_seq_first_ = header.sequenceNumber;
-    received_seq_max_ = header.sequenceNumber;
-    received_inorder_packet_count_ = 1;
-    clock_->CurrentNtp(last_receive_time_secs_, last_receive_time_frac_);
-    last_receive_time_ms_ = clock_->TimeInMilliseconds();
-    return;
   }
 
   // Count only the new packets received. That is, if packets 1, 2, 3, 5, 4, 6
@@ -99,66 +93,27 @@
     uint32_t receive_time_secs;
     uint32_t receive_time_frac;
     clock_->CurrentNtp(receive_time_secs, receive_time_frac);
-    received_inorder_packet_count_++;
 
     // Wrong if we use RetransmitOfOldPacket.
-    int32_t seq_diff = header.sequenceNumber - received_seq_max_;
-    if (seq_diff < 0) {
+    if (receive_counters_.packets > 1 &&
+        received_seq_max_ > header.sequenceNumber) {
       // Wrap around detected.
       received_seq_wraps_++;
     }
     // New max.
     received_seq_max_ = header.sequenceNumber;
 
+    // If new time stamp and more than one in-order packet received, calculate
+    // new jitter statistics.
     if (header.timestamp != last_received_timestamp_ &&
-        received_inorder_packet_count_ > 1) {
-      uint32_t receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
-          receive_time_secs, receive_time_frac, header.payload_type_frequency);
-      uint32_t last_receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
-          last_receive_time_secs_, last_receive_time_frac_,
-          header.payload_type_frequency);
-      int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
-          (header.timestamp - last_received_timestamp_);
-
-      time_diff_samples = abs(time_diff_samples);
-
-      // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
-      // If this happens, don't update jitter value. Use 5 secs video frequency
-      // as the threshold.
-      if (time_diff_samples < 450000) {
-        // Note we calculate in Q4 to avoid using float.
-        int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
-        jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
-      }
-
-      // Extended jitter report, RFC 5450.
-      // Actual network jitter, excluding the source-introduced jitter.
-      int32_t time_diff_samples_ext =
-        (receive_time_rtp - last_receive_time_rtp) -
-        ((header.timestamp +
-          header.extension.transmissionTimeOffset) -
-         (last_received_timestamp_ +
-          last_received_transmission_time_offset_));
-
-      time_diff_samples_ext = abs(time_diff_samples_ext);
-
-      if (time_diff_samples_ext < 450000) {
-        int32_t jitter_diffQ4TransmissionTimeOffset =
-          (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
-        jitter_q4_transmission_time_offset_ +=
-          ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
-      }
+        (receive_counters_.packets - receive_counters_.retransmitted_packets) >
+            1) {
+      UpdateJitter(header, receive_time_secs, receive_time_frac);
     }
     last_received_timestamp_ = header.timestamp;
     last_receive_time_secs_ = receive_time_secs;
     last_receive_time_frac_ = receive_time_frac;
     last_receive_time_ms_ = clock_->TimeInMilliseconds();
-  } else {
-    if (retransmitted) {
-      received_retransmitted_packets_++;
-    } else {
-      received_inorder_packet_count_++;
-    }
   }
 
   uint16_t packet_oh = header.headerLength + header.paddingLength;
@@ -166,6 +121,55 @@
   // Our measured overhead. Filter from RFC 5104 4.2.1.2:
   // avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH,
   received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
+
+  rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
+}
+
+void StreamStatisticianImpl::UpdateJitter(const RTPHeader& header,
+                                          uint32_t receive_time_secs,
+                                          uint32_t receive_time_frac) {
+  uint32_t receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
+          receive_time_secs, receive_time_frac, header.payload_type_frequency);
+  uint32_t last_receive_time_rtp = ModuleRTPUtility::ConvertNTPTimeToRTP(
+      last_receive_time_secs_, last_receive_time_frac_,
+      header.payload_type_frequency);
+  int32_t time_diff_samples = (receive_time_rtp - last_receive_time_rtp) -
+      (header.timestamp - last_received_timestamp_);
+
+  time_diff_samples = abs(time_diff_samples);
+
+  // lib_jingle sometimes deliver crazy jumps in TS for the same stream.
+  // If this happens, don't update jitter value. Use 5 secs video frequency
+  // as the threshold.
+  if (time_diff_samples < 450000) {
+    // Note we calculate in Q4 to avoid using float.
+    int32_t jitter_diff_q4 = (time_diff_samples << 4) - jitter_q4_;
+    jitter_q4_ += ((jitter_diff_q4 + 8) >> 4);
+  }
+
+  // Extended jitter report, RFC 5450.
+  // Actual network jitter, excluding the source-introduced jitter.
+  int32_t time_diff_samples_ext =
+    (receive_time_rtp - last_receive_time_rtp) -
+    ((header.timestamp +
+      header.extension.transmissionTimeOffset) -
+     (last_received_timestamp_ +
+      last_received_transmission_time_offset_));
+
+  time_diff_samples_ext = abs(time_diff_samples_ext);
+
+  if (time_diff_samples_ext < 450000) {
+    int32_t jitter_diffQ4TransmissionTimeOffset =
+      (time_diff_samples_ext << 4) - jitter_q4_transmission_time_offset_;
+    jitter_q4_transmission_time_offset_ +=
+      ((jitter_diffQ4TransmissionTimeOffset + 8) >> 4);
+  }
+}
+
+void StreamStatisticianImpl::FecPacketReceived() {
+  CriticalSectionScoped cs(stream_lock_.get());
+  ++receive_counters_.fec_packets;
+  rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
 }
 
 void StreamStatisticianImpl::SetMaxReorderingThreshold(
@@ -178,7 +182,7 @@
                                            bool reset) {
   {
     CriticalSectionScoped cs(stream_lock_.get());
-    if (received_seq_first_ == 0 && received_byte_count_ == 0) {
+    if (received_seq_first_ == 0 && receive_counters_.bytes == 0) {
       // We have not received anything.
       return false;
     }
@@ -197,6 +201,8 @@
   }
 
   rtcp_callback_->StatisticsUpdated(*statistics, ssrc_);
+  rtp_callback_->DataCountersUpdated(receive_counters_, ssrc_);
+
   return true;
 }
 
@@ -219,7 +225,8 @@
   // Number of received RTP packets since last report, counts all packets but
   // not re-transmissions.
   uint32_t rec_since_last =
-      received_inorder_packet_count_ - last_report_inorder_packets_;
+      (receive_counters_.packets - receive_counters_.retransmitted_packets) -
+      last_report_inorder_packets_;
 
   // With NACK we don't know the expected retransmissions during the last
   // second. We know how many "old" packets we have received. We just count
@@ -231,7 +238,7 @@
   // re-transmitted. We use RTT to decide if a packet is re-ordered or
   // re-transmitted.
   uint32_t retransmitted_packets =
-      received_retransmitted_packets_ - last_report_old_packets_;
+      receive_counters_.retransmitted_packets - last_report_old_packets_;
   rec_since_last += retransmitted_packets;
 
   int32_t missing = 0;
@@ -258,8 +265,9 @@
   last_reported_statistics_ = stats;
 
   // Only for report blocks in RTCP SR and RR.
-  last_report_inorder_packets_ = received_inorder_packet_count_;
-  last_report_old_packets_ = received_retransmitted_packets_;
+  last_report_inorder_packets_ =
+      receive_counters_.packets - receive_counters_.retransmitted_packets;
+  last_report_old_packets_ = receive_counters_.retransmitted_packets;
   last_report_seq_max_ = received_seq_max_;
 
   return stats;
@@ -269,11 +277,11 @@
     uint32_t* bytes_received, uint32_t* packets_received) const {
   CriticalSectionScoped cs(stream_lock_.get());
   if (bytes_received) {
-    *bytes_received = received_byte_count_;
+    *bytes_received = receive_counters_.bytes + receive_counters_.header_bytes +
+                      receive_counters_.padding_bytes;
   }
   if (packets_received) {
-    *packets_received =
-        received_retransmitted_packets_ + received_inorder_packet_count_;
+    *packets_received = receive_counters_.packets;
   }
 }
 
@@ -358,7 +366,8 @@
     : clock_(clock),
       receive_statistics_lock_(CriticalSectionWrapper::CreateCriticalSection()),
       last_rate_update_ms_(0),
-      rtcp_stats_callback_(NULL) {}
+      rtcp_stats_callback_(NULL),
+      rtp_stats_callback_(NULL) {}
 
 ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
   while (!statisticians_.empty()) {
@@ -368,7 +377,8 @@
 }
 
 void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
-                                           size_t bytes, bool old_packet) {
+                                           size_t bytes,
+                                           bool retransmitted) {
   StatisticianImplMap::iterator it;
   {
     CriticalSectionScoped cs(receive_statistics_lock_.get());
@@ -376,11 +386,18 @@
     if (it == statisticians_.end()) {
       std::pair<StatisticianImplMap::iterator, uint32_t> insert_result =
           statisticians_.insert(std::make_pair(
-              header.ssrc, new StreamStatisticianImpl(clock_, this)));
+              header.ssrc, new StreamStatisticianImpl(clock_, this, this)));
       it = insert_result.first;
     }
   }
-  it->second->IncomingPacket(header, bytes, old_packet);
+  it->second->IncomingPacket(header, bytes, retransmitted);
+}
+
+void ReceiveStatisticsImpl::FecPacketReceived(uint32_t ssrc) {
+  CriticalSectionScoped cs(receive_statistics_lock_.get());
+  StatisticianImplMap::iterator it = statisticians_.find(ssrc);
+  assert(it != statisticians_.end());
+  it->second->FecPacketReceived();
 }
 
 void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) {
@@ -461,10 +478,28 @@
   }
 }
 
+void ReceiveStatisticsImpl::RegisterRtpStatisticsCallback(
+    StreamDataCountersCallback* callback) {
+  CriticalSectionScoped cs(receive_statistics_lock_.get());
+  if (callback != NULL)
+    assert(rtp_stats_callback_ == NULL);
+  rtp_stats_callback_ = callback;
+}
+
+void ReceiveStatisticsImpl::DataCountersUpdated(const StreamDataCounters& stats,
+                                                uint32_t ssrc) {
+  CriticalSectionScoped cs(receive_statistics_lock_.get());
+  if (rtp_stats_callback_) {
+    rtp_stats_callback_->DataCountersUpdated(stats, ssrc);
+  }
+}
+
 void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
                                            size_t bytes,
                                            bool retransmitted) {}
 
+void NullReceiveStatistics::FecPacketReceived(uint32_t ssrc) {}
+
 StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const {
   return StatisticianMap();
 }
@@ -484,4 +519,7 @@
 void NullReceiveStatistics::RegisterRtcpStatisticsCallback(
     RtcpStatisticsCallback* callback) {}
 
+void NullReceiveStatistics::RegisterRtpStatisticsCallback(
+    StreamDataCountersCallback* 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 0e24ca2..34bd46c 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -25,7 +25,9 @@
 
 class StreamStatisticianImpl : public StreamStatistician {
  public:
-  StreamStatisticianImpl(Clock* clock, RtcpStatisticsCallback* rtcp_callback);
+  StreamStatisticianImpl(Clock* clock,
+                         RtcpStatisticsCallback* rtcp_callback,
+                         StreamDataCountersCallback* rtp_callback);
   virtual ~StreamStatisticianImpl() {}
 
   virtual bool GetStatistics(RtcpStatistics* statistics, bool reset) OVERRIDE;
@@ -34,11 +36,13 @@
   virtual uint32_t BitrateReceived() const OVERRIDE;
   virtual void ResetStatistics() OVERRIDE;
   virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
-                                     int min_rtt) const OVERRIDE;
+                                       int min_rtt) const OVERRIDE;
   virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE;
 
-  void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
+  void IncomingPacket(const RTPHeader& rtp_header,
+                      size_t bytes,
                       bool retransmitted);
+  void FecPacketReceived();
   void SetMaxReorderingThreshold(int max_reordering_threshold);
   void ProcessBitrate();
   virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const;
@@ -46,6 +50,9 @@
  private:
   bool InOrderPacketInternal(uint16_t sequence_number) const;
   RtcpStatistics CalculateStatistics();
+  void UpdateJitter(const RTPHeader& header,
+                    uint32_t receive_time_secs,
+                    uint32_t receive_time_frac);
 
   Clock* clock_;
   scoped_ptr<CriticalSectionWrapper> stream_lock_;
@@ -66,13 +73,10 @@
   uint16_t received_seq_first_;
   uint16_t received_seq_max_;
   uint16_t received_seq_wraps_;
-  bool first_packet_;
 
   // Current counter values.
   uint16_t received_packet_overhead_;
-  uint32_t received_byte_count_;
-  uint32_t received_retransmitted_packets_;
-  uint32_t received_inorder_packet_count_;
+  StreamDataCounters receive_counters_;
 
   // Counter values when we sent the last report.
   uint32_t last_report_inorder_packets_;
@@ -81,18 +85,22 @@
   RtcpStatistics last_reported_statistics_;
 
   RtcpStatisticsCallback* const rtcp_callback_;
+  StreamDataCountersCallback* const rtp_callback_;
 };
 
 class ReceiveStatisticsImpl : public ReceiveStatistics,
-                              public RtcpStatisticsCallback {
+                              public RtcpStatisticsCallback,
+                              public StreamDataCountersCallback {
  public:
   explicit ReceiveStatisticsImpl(Clock* clock);
 
   ~ReceiveStatisticsImpl();
 
   // Implement ReceiveStatistics.
-  virtual void IncomingPacket(const RTPHeader& header, size_t bytes,
-                              bool old_packet) OVERRIDE;
+  virtual void IncomingPacket(const RTPHeader& header,
+                              size_t bytes,
+                              bool retransmitted) OVERRIDE;
+  virtual void FecPacketReceived(uint32_t ssrc) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
   virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
   virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
@@ -106,10 +114,15 @@
   virtual void RegisterRtcpStatisticsCallback(RtcpStatisticsCallback* callback)
       OVERRIDE;
 
-  virtual void StatisticsUpdated(const RtcpStatistics& statistics,
-                                 uint32_t ssrc) OVERRIDE;
+  virtual void RegisterRtpStatisticsCallback(
+      StreamDataCountersCallback* callback) OVERRIDE;
 
  private:
+  virtual void StatisticsUpdated(const RtcpStatistics& statistics,
+                                 uint32_t ssrc) OVERRIDE;
+  virtual void DataCountersUpdated(const StreamDataCounters& counters,
+                                   uint32_t ssrc) OVERRIDE;
+
   typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
 
   Clock* clock_;
@@ -118,6 +131,7 @@
   StatisticianImplMap statisticians_;
 
   RtcpStatisticsCallback* rtcp_stats_callback_;
+  StreamDataCountersCallback* rtp_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 6969bbc..f0b9ded 100644
--- a/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -28,10 +28,10 @@
       receive_statistics_(ReceiveStatistics::Create(&clock_)) {
     memset(&header1_, 0, sizeof(header1_));
     header1_.ssrc = kSsrc1;
-    header1_.sequenceNumber = 0;
+    header1_.sequenceNumber = 100;
     memset(&header2_, 0, sizeof(header2_));
     header2_.ssrc = kSsrc2;
-    header2_.sequenceNumber = 0;
+    header2_.sequenceNumber = 100;
   }
 
  protected:
@@ -132,7 +132,7 @@
   EXPECT_EQ(2u, packets_received);
 }
 
-TEST_F(ReceiveStatisticsTest, Callbacks) {
+TEST_F(ReceiveStatisticsTest, RtcpCallbacks) {
   class TestCallback : public RtcpStatisticsCallback {
    public:
     TestCallback()
@@ -186,6 +186,10 @@
             callback.stats_.extended_max_sequence_number);
   EXPECT_EQ(statistics.fraction_lost, callback.stats_.fraction_lost);
   EXPECT_EQ(statistics.jitter, callback.stats_.jitter);
+  EXPECT_EQ(51, statistics.fraction_lost);
+  EXPECT_EQ(1u, statistics.cumulative_lost);
+  EXPECT_EQ(5u, statistics.extended_max_sequence_number);
+  EXPECT_EQ(4u, statistics.jitter);
 
   receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
 
@@ -214,4 +218,86 @@
   // Should not have been called after deregister.
   EXPECT_EQ(1u, callback.num_calls_);
 }
+
+TEST_F(ReceiveStatisticsTest, RtpCallbacks) {
+  class TestCallback : public StreamDataCountersCallback {
+   public:
+    TestCallback()
+        : StreamDataCountersCallback(), num_calls_(0), ssrc_(0), stats_() {}
+    virtual ~TestCallback() {}
+
+    virtual void DataCountersUpdated(const StreamDataCounters& counters,
+                                     uint32_t ssrc) {
+      ssrc_ = ssrc;
+      stats_ = counters;
+      ++num_calls_;
+    }
+
+    void ExpectMatches(uint32_t num_calls,
+                       uint32_t ssrc,
+                       uint32_t bytes,
+                       uint32_t padding,
+                       uint32_t packets,
+                       uint32_t retransmits,
+                       uint32_t fec) {
+      EXPECT_EQ(num_calls, num_calls_);
+      EXPECT_EQ(ssrc, ssrc_);
+      EXPECT_EQ(bytes, stats_.bytes);
+      EXPECT_EQ(padding, stats_.padding_bytes);
+      EXPECT_EQ(packets, stats_.packets);
+      EXPECT_EQ(retransmits, stats_.retransmitted_packets);
+      EXPECT_EQ(fec, stats_.fec_packets);
+    }
+
+    uint32_t num_calls_;
+    uint32_t ssrc_;
+    StreamDataCounters stats_;
+  } callback;
+
+  receive_statistics_->RegisterRtpStatisticsCallback(&callback);
+
+  const uint32_t kHeaderLength = 20;
+  const uint32_t kPaddingLength = 9;
+
+  // One packet of size kPacketSize1.
+  header1_.headerLength = kHeaderLength;
+  receive_statistics_->IncomingPacket(
+      header1_, kPacketSize1 + kHeaderLength, false);
+  callback.ExpectMatches(1, kSsrc1, kPacketSize1, 0, 1, 0, 0);
+
+  ++header1_.sequenceNumber;
+  clock_.AdvanceTimeMilliseconds(5);
+  header1_.paddingLength = 9;
+  // Another packet of size kPacketSize1 with 9 bytes padding.
+  receive_statistics_->IncomingPacket(
+      header1_, kPacketSize1 + kHeaderLength + kPaddingLength, false);
+  callback.ExpectMatches(2, kSsrc1, 2 * kPacketSize1, kPaddingLength, 2, 0, 0);
+
+  clock_.AdvanceTimeMilliseconds(5);
+  // Retransmit last packet.
+  receive_statistics_->IncomingPacket(
+      header1_, kPacketSize1 + kHeaderLength + kPaddingLength, true);
+  callback.ExpectMatches(
+      3, kSsrc1, 3 * kPacketSize1, kPaddingLength * 2, 3, 1, 0);
+
+  header1_.paddingLength = 0;
+  ++header1_.sequenceNumber;
+  clock_.AdvanceTimeMilliseconds(5);
+  // One recovered packet.
+  receive_statistics_->IncomingPacket(
+      header1_, kPacketSize1 + kHeaderLength, false);
+  receive_statistics_->FecPacketReceived(kSsrc1);
+  callback.ExpectMatches(
+      5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+
+  receive_statistics_->RegisterRtpStatisticsCallback(NULL);
+
+  // New stats, but callback should not be called.
+  ++header1_.sequenceNumber;
+  clock_.AdvanceTimeMilliseconds(5);
+  receive_statistics_->IncomingPacket(
+      header1_, kPacketSize1 + kHeaderLength, true);
+  callback.ExpectMatches(
+      5, kSsrc1, 4 * kPacketSize1, kPaddingLength * 2, 4, 1, 1);
+}
 }  // namespace webrtc
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index c0439e7..13c0a5c 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -1372,6 +1372,16 @@
   }
 }
 
+void ViEChannel::RegisterReceiveChannelRtpStatisticsCallback(
+    StreamDataCountersCallback* callback) {
+  WEBRTC_TRACE(kTraceInfo,
+               kTraceVideo,
+               ViEId(engine_id_, channel_id_),
+               "%s",
+               __FUNCTION__);
+  vie_receiver_.GetReceiveStatistics()->RegisterRtpStatisticsCallback(callback);
+}
+
 void ViEChannel::GetBandwidthUsage(uint32_t* total_bitrate_sent,
                                    uint32_t* video_bitrate_sent,
                                    uint32_t* fec_bitrate_sent,
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 9313fdc..be961d5 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -199,6 +199,10 @@
   void RegisterSendChannelRtpStatisticsCallback(
       StreamDataCountersCallback* callback);
 
+  // Called on update of RTP statistics.
+  void RegisterReceiveChannelRtpStatisticsCallback(
+      StreamDataCountersCallback* callback);
+
   void GetBandwidthUsage(uint32_t* total_bitrate_sent,
                          uint32_t* video_bitrate_sent,
                          uint32_t* fec_bitrate_sent,
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index 2946c4a..0d88014 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -269,11 +269,13 @@
   header.payload_type_frequency = kVideoPayloadTypeFrequency;
 
   bool in_order = IsPacketInOrder(header);
-  rtp_receive_statistics_->IncomingPacket(header, received_packet_length,
-      IsPacketRetransmitted(header, in_order));
+  rtp_receive_statistics_->IncomingPacket(
+      header, received_packet_length, IsPacketRetransmitted(header, in_order));
   rtp_payload_registry_->SetIncomingPayloadType(header);
-  return ReceivePacket(received_packet, received_packet_length, header,
-                       in_order) ? 0 : -1;
+  return ReceivePacket(
+             received_packet, received_packet_length, header, in_order)
+             ? 0
+             : -1;
 }
 
 bool ViEReceiver::ReceivePacket(const uint8_t* packet,
@@ -299,9 +301,11 @@
                                                     int packet_length,
                                                     const RTPHeader& header) {
   if (rtp_payload_registry_->IsRed(header)) {
+    int8_t ulpfec_pt = rtp_payload_registry_->ulpfec_payload_type();
+    if (packet[header.headerLength] == ulpfec_pt)
+      rtp_receive_statistics_->FecPacketReceived(header.ssrc);
     if (fec_receiver_->AddReceivedRedPacket(
-        header, packet, packet_length,
-        rtp_payload_registry_->ulpfec_payload_type()) != 0) {
+            header, packet, packet_length, ulpfec_pt) != 0) {
       WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
                    "Incoming RED packet error");
       return false;
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index 6f480cb..5cccb51 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -90,8 +90,10 @@
  private:
   int InsertRTPPacket(const int8_t* rtp_packet, int rtp_packet_length,
                       const PacketTime& packet_time);
-  bool ReceivePacket(const uint8_t* packet, int packet_length,
-                     const RTPHeader& header, bool in_order);
+  bool ReceivePacket(const uint8_t* packet,
+                     int packet_length,
+                     const RTPHeader& header,
+                     bool in_order);
   // Parses and handles for instance RTX and RED headers.
   // This function assumes that it's being called from only one thread.
   bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index d9f2211..b655349 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -1224,15 +1224,35 @@
 }
 
 int ViERTP_RTCPImpl::RegisterReceiveChannelRtpStatisticsCallback(
-    int channel, StreamDataCountersCallback* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    StreamDataCountersCallback* 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->RegisterReceiveChannelRtpStatisticsCallback(callback);
+  return 0;
 }
 
 int ViERTP_RTCPImpl::DeregisterReceiveChannelRtpStatisticsCallback(
-    int channel, StreamDataCountersCallback* callback) {
-  // TODO(sprang): Implement
-  return -1;
+    const int video_channel,
+    StreamDataCountersCallback* 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->RegisterReceiveChannelRtpStatisticsCallback(NULL);
+  return 0;
 }
 
 // Called whenever the send bitrate is updated.