Add statistics gathering for packet loss.

Adds a class used to classify whether packet loss events are a single packet or multiple packets as well as how many packets have been lost. Also exposes a new function in the RtpRtcp interface to retrieve these statistics.

BUG=

Review URL: https://codereview.webrtc.org/1198853004

Cr-Commit-Position: refs/heads/master@{#9568}
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index af4c97b..7f3a1e8 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -230,6 +230,7 @@
             'rtp_rtcp/source/fec_test_helper.h',
             'rtp_rtcp/source/h264_sps_parser_unittest.cc',
             'rtp_rtcp/source/nack_rtx_unittest.cc',
+            'rtp_rtcp/source/packet_loss_stats_unittest.cc',
             'rtp_rtcp/source/producer_fec_unittest.cc',
             'rtp_rtcp/source/receive_statistics_unittest.cc',
             'rtp_rtcp/source/remote_ntp_time_estimator_unittest.cc',
diff --git a/webrtc/modules/rtp_rtcp/BUILD.gn b/webrtc/modules/rtp_rtcp/BUILD.gn
index 0eda287..ca4b812 100644
--- a/webrtc/modules/rtp_rtcp/BUILD.gn
+++ b/webrtc/modules/rtp_rtcp/BUILD.gn
@@ -35,6 +35,8 @@
     "source/h264_sps_parser.cc",
     "source/h264_sps_parser.h",
     "source/mock/mock_rtp_payload_strategy.h",
+    "source/packet_loss_stats.cc",
+    "source/packet_loss_stats.h",
     "source/producer_fec.cc",
     "source/producer_fec.h",
     "source/receive_statistics_impl.cc",
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
index 98f7c26..7a96088 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -431,6 +431,14 @@
         StreamDataCounters* rtx_counters) const = 0;
 
     /*
+     *  Get packet loss statistics for the RTP stream.
+     */
+    virtual void GetRtpPacketLossStats(
+        bool outgoing,
+        uint32_t ssrc,
+        struct RtpPacketLossStats* loss_stats) const = 0;
+
+    /*
     *   Get received RTCP sender info
     *
     *   return -1 on failure else 0
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index ed7dfe0..ea36687 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -350,5 +350,18 @@
                             const uint8_t volume) override {}
 };
 
+// Statistics about packet loss for a single directional connection. All values
+// are totals since the connection initiated.
+struct RtpPacketLossStats {
+  // The number of packets lost in events where no adjacent packets were also
+  // lost.
+  uint64_t single_packet_loss_count;
+  // The number of events in which more than one adjacent packet was lost.
+  uint64_t multiple_packet_loss_event_count;
+  // The number of packets lost in events where more than one adjacent packet
+  // was lost.
+  uint64_t multiple_packet_loss_packet_count;
+};
+
 }  // namespace webrtc
 #endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_RTP_RTCP_DEFINES_H_
diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 99e5b1c..127a795 100644
--- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -165,6 +165,8 @@
       int32_t(size_t *bytesSent, uint32_t *packetsSent));
   MOCK_CONST_METHOD2(GetSendStreamDataCounters,
       void(StreamDataCounters*, StreamDataCounters*));
+  MOCK_CONST_METHOD3(GetRtpPacketLossStats,
+      void(bool, uint32_t, struct RtpPacketLossStats*));
   MOCK_METHOD1(RemoteRTCPStat,
       int32_t(RTCPSenderInfo* senderInfo));
   MOCK_CONST_METHOD1(RemoteRTCPStat,
diff --git a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
index e73b43a..dcd47df 100644
--- a/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
+++ b/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
@@ -31,6 +31,8 @@
         'source/byte_io.h',
         'source/fec_receiver_impl.cc',
         'source/fec_receiver_impl.h',
+        'source/packet_loss_stats.cc',
+        'source/packet_loss_stats.h',
         'source/receive_statistics_impl.cc',
         'source/receive_statistics_impl.h',
         'source/remote_ntp_time_estimator.cc',
diff --git a/webrtc/modules/rtp_rtcp/source/packet_loss_stats.cc b/webrtc/modules/rtp_rtcp/source/packet_loss_stats.cc
new file mode 100644
index 0000000..4ab3864
--- /dev/null
+++ b/webrtc/modules/rtp_rtcp/source/packet_loss_stats.cc
@@ -0,0 +1,137 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/rtp_rtcp/source/packet_loss_stats.h"
+
+#include <vector>
+
+#include "webrtc/base/checks.h"
+
+// After this many packets are added, adding additional packets will cause the
+// oldest packets to be pruned from the buffer.
+static const int kBufferSize = 100;
+
+namespace webrtc {
+
+PacketLossStats::PacketLossStats()
+    : single_loss_historic_count_(0),
+      multiple_loss_historic_event_count_(0),
+      multiple_loss_historic_packet_count_(0) {
+}
+
+void PacketLossStats::AddLostPacket(uint16_t sequence_number) {
+  // Detect sequence number wrap around.
+  if (!lost_packets_buffer_.empty() &&
+      static_cast<int>(*(lost_packets_buffer_.rbegin())) - sequence_number
+      > 0x8000) {
+    // The buffer contains large numbers and this is a small number.
+    lost_packets_wrapped_buffer_.insert(sequence_number);
+  } else {
+    lost_packets_buffer_.insert(sequence_number);
+  }
+  if (lost_packets_wrapped_buffer_.size() + lost_packets_buffer_.size()
+      > kBufferSize || (!lost_packets_wrapped_buffer_.empty() &&
+                        *(lost_packets_wrapped_buffer_.rbegin()) > 0x4000)) {
+    PruneBuffer();
+  }
+}
+
+int PacketLossStats::GetSingleLossCount() const {
+  int single_loss_count, unused1, unused2;
+  ComputeLossCounts(&single_loss_count, &unused1, &unused2);
+  return single_loss_count;
+}
+
+int PacketLossStats::GetMultipleLossEventCount() const {
+  int event_count, unused1, unused2;
+  ComputeLossCounts(&unused1, &event_count, &unused2);
+  return event_count;
+}
+
+int PacketLossStats::GetMultipleLossPacketCount() const {
+  int packet_count, unused1, unused2;
+  ComputeLossCounts(&unused1, &unused2, &packet_count);
+  return packet_count;
+}
+
+void PacketLossStats::ComputeLossCounts(
+    int* out_single_loss_count,
+    int* out_multiple_loss_event_count,
+    int* out_multiple_loss_packet_count) const {
+  *out_single_loss_count = single_loss_historic_count_;
+  *out_multiple_loss_event_count = multiple_loss_historic_event_count_;
+  *out_multiple_loss_packet_count = multiple_loss_historic_packet_count_;
+  if (lost_packets_buffer_.empty()) {
+    DCHECK(lost_packets_wrapped_buffer_.empty());
+    return;
+  }
+  uint16_t last_num = 0;
+  int sequential_count = 0;
+  std::vector<const std::set<uint16_t>*> buffers;
+  buffers.push_back(&lost_packets_buffer_);
+  buffers.push_back(&lost_packets_wrapped_buffer_);
+  for (auto buffer : buffers) {
+    for (auto it = buffer->begin(); it != buffer->end(); ++it) {
+      uint16_t current_num = *it;
+      if (sequential_count > 0 && current_num != ((last_num + 1) & 0xFFFF)) {
+        if (sequential_count == 1) {
+          (*out_single_loss_count)++;
+        } else {
+          (*out_multiple_loss_event_count)++;
+          *out_multiple_loss_packet_count += sequential_count;
+        }
+        sequential_count = 0;
+      }
+      sequential_count++;
+      last_num = current_num;
+    }
+  }
+  if (sequential_count == 1) {
+    (*out_single_loss_count)++;
+  } else if (sequential_count > 1) {
+    (*out_multiple_loss_event_count)++;
+    *out_multiple_loss_packet_count += sequential_count;
+  }
+}
+
+void PacketLossStats::PruneBuffer() {
+  // Remove the oldest lost packet and any contiguous packets and move them
+  // into the historic counts.
+  auto it = lost_packets_buffer_.begin();
+  uint16_t last_removed = 0;
+  int remove_count = 0;
+  // Count adjacent packets and continue counting if it is wrap around by
+  // swapping in the wrapped buffer and letting our value wrap as well.
+  while (remove_count == 0 || (!lost_packets_buffer_.empty() &&
+                               *it == ((last_removed + 1) & 0xFFFF))) {
+    last_removed = *it;
+    remove_count++;
+    auto to_erase = it++;
+    lost_packets_buffer_.erase(to_erase);
+    if (lost_packets_buffer_.empty()) {
+      lost_packets_buffer_.swap(lost_packets_wrapped_buffer_);
+      it = lost_packets_buffer_.begin();
+    }
+  }
+  if (remove_count > 1) {
+    multiple_loss_historic_event_count_++;
+    multiple_loss_historic_packet_count_ += remove_count;
+  } else {
+    single_loss_historic_count_++;
+  }
+  // Continue pruning if the wrapped buffer is beyond a threshold and there are
+  // things left in the pre-wrapped buffer.
+  if (!lost_packets_wrapped_buffer_.empty() &&
+      *(lost_packets_wrapped_buffer_.rbegin()) > 0x4000) {
+    PruneBuffer();
+  }
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/packet_loss_stats.h b/webrtc/modules/rtp_rtcp/source/packet_loss_stats.h
new file mode 100644
index 0000000..2eab043
--- /dev/null
+++ b/webrtc/modules/rtp_rtcp/source/packet_loss_stats.h
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_
+
+#include <stdint.h>
+#include <set>
+
+namespace webrtc {
+
+// Keeps track of statistics of packet loss including whether losses are a
+// single packet or multiple packets in a row.
+class PacketLossStats {
+ public:
+  PacketLossStats();
+  ~PacketLossStats() {}
+
+  // Adds a lost packet to the stats by sequence number.
+  void AddLostPacket(uint16_t sequence_number);
+
+  // Queries the number of packets that were lost by themselves, no neighboring
+  // packets were lost.
+  int GetSingleLossCount() const;
+
+  // Queries the number of times that multiple packets with sequential numbers
+  // were lost. This is the number of events with more than one packet lost,
+  // regardless of the size of the event;
+  int GetMultipleLossEventCount() const;
+
+  // Queries the number of packets lost in multiple packet loss events. Combined
+  // with the event count, this can be used to determine the average event size.
+  int GetMultipleLossPacketCount() const;
+
+ private:
+  std::set<uint16_t> lost_packets_buffer_;
+  std::set<uint16_t> lost_packets_wrapped_buffer_;
+  int single_loss_historic_count_;
+  int multiple_loss_historic_event_count_;
+  int multiple_loss_historic_packet_count_;
+
+  void ComputeLossCounts(int* out_single_loss_count,
+                         int* out_multiple_loss_event_count,
+                         int* out_multiple_loss_packet_count) const;
+  void PruneBuffer();
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_PACKET_LOSS_STATS_H_
diff --git a/webrtc/modules/rtp_rtcp/source/packet_loss_stats_unittest.cc b/webrtc/modules/rtp_rtcp/source/packet_loss_stats_unittest.cc
new file mode 100644
index 0000000..6606282
--- /dev/null
+++ b/webrtc/modules/rtp_rtcp/source/packet_loss_stats_unittest.cc
@@ -0,0 +1,197 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/rtp_rtcp/source/packet_loss_stats.h"
+
+namespace webrtc {
+
+class PacketLossStatsTest : public ::testing::Test {
+ protected:
+  PacketLossStats stats_;
+};
+
+// Add a lost packet as every other packet, they should all count as single
+// losses.
+TEST_F(PacketLossStatsTest, EveryOtherPacket) {
+  for (int i = 0; i < 1000; i += 2) {
+    stats_.AddLostPacket(i);
+  }
+  EXPECT_EQ(500, stats_.GetSingleLossCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossPacketCount());
+}
+
+// Add a lost packet as every other packet, but such that the sequence numbers
+// will wrap around while they are being added.
+TEST_F(PacketLossStatsTest, EveryOtherPacketWrapped) {
+  for (int i = 65500; i < 66500; i += 2) {
+    stats_.AddLostPacket(i & 0xFFFF);
+  }
+  EXPECT_EQ(500, stats_.GetSingleLossCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossPacketCount());
+}
+
+// Add a lost packet as every other packet, but such that the sequence numbers
+// will wrap around close to the very end, such that the buffer contains packets
+// on either side of the wrapping.
+TEST_F(PacketLossStatsTest, EveryOtherPacketWrappedAtEnd) {
+  for (int i = 64600; i < 65600; i += 2) {
+    stats_.AddLostPacket(i & 0xFFFF);
+  }
+  EXPECT_EQ(500, stats_.GetSingleLossCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(0, stats_.GetMultipleLossPacketCount());
+}
+
+// Add a lost packet as the first three of every eight packets. Each set of
+// three should count as a multiple loss event and three multiple loss packets.
+TEST_F(PacketLossStatsTest, FirstThreeOfEight) {
+  for (int i = 0; i < 1000; ++i) {
+    if ((i & 7) < 3) {
+      stats_.AddLostPacket(i);
+    }
+  }
+  EXPECT_EQ(0, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add a lost packet as the first three of every eight packets such that the
+// sequence numbers wrap in the middle of adding them.
+TEST_F(PacketLossStatsTest, FirstThreeOfEightWrapped) {
+  for (int i = 65500; i < 66500; ++i) {
+    if ((i & 7) < 3) {
+      stats_.AddLostPacket(i & 0xFFFF);
+    }
+  }
+  EXPECT_EQ(0, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add a lost packet as the first three of every eight packets such that the
+// sequence numbers wrap near the end of adding them and there are still numbers
+// in the buffer from before the wrapping.
+TEST_F(PacketLossStatsTest, FirstThreeOfEightWrappedAtEnd) {
+  for (int i = 64600; i < 65600; ++i) {
+    if ((i & 7) < 3) {
+      stats_.AddLostPacket(i & 0xFFFF);
+    }
+  }
+  EXPECT_EQ(0, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets as the first three and the fifth of every eight packets. The
+// set of three should be multiple loss and the fifth should be single loss.
+TEST_F(PacketLossStatsTest, FirstThreeAndFifthOfEight) {
+  for (int i = 0; i < 1000; ++i) {
+    if ((i & 7) < 3 || (i & 7) == 4) {
+      stats_.AddLostPacket(i);
+    }
+  }
+  EXPECT_EQ(125, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets as the first three and the fifth of every eight packets such
+// that the sequence numbers wrap in the middle of adding them.
+TEST_F(PacketLossStatsTest, FirstThreeAndFifthOfEightWrapped) {
+  for (int i = 65500; i < 66500; ++i) {
+    if ((i & 7) < 3 || (i & 7) == 4) {
+      stats_.AddLostPacket(i & 0xFFFF);
+    }
+  }
+  EXPECT_EQ(125, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets as the first three and the fifth of every eight packets such
+// that the sequence numbers wrap near the end of adding them and there are
+// packets from before the wrapping still in the buffer.
+TEST_F(PacketLossStatsTest, FirstThreeAndFifthOfEightWrappedAtEnd) {
+  for (int i = 64600; i < 65600; ++i) {
+    if ((i & 7) < 3 || (i & 7) == 4) {
+      stats_.AddLostPacket(i & 0xFFFF);
+    }
+  }
+  EXPECT_EQ(125, stats_.GetSingleLossCount());
+  EXPECT_EQ(125, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(375, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets such that there is a multiple loss event that continues
+// around the wrapping of sequence numbers.
+TEST_F(PacketLossStatsTest, MultipleLossEventWrapped) {
+  for (int i = 60000; i < 60500; i += 2) {
+    stats_.AddLostPacket(i);
+  }
+  for (int i = 65530; i < 65540; ++i) {
+    stats_.AddLostPacket(i & 0xFFFF);
+  }
+  EXPECT_EQ(250, stats_.GetSingleLossCount());
+  EXPECT_EQ(1, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(10, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets such that there is a multiple loss event that continues
+// around the wrapping of sequence numbers and then is pushed out of the buffer.
+TEST_F(PacketLossStatsTest, MultipleLossEventWrappedPushedOut) {
+  for (int i = 60000; i < 60500; i += 2) {
+    stats_.AddLostPacket(i);
+  }
+  for (int i = 65530; i < 65540; ++i) {
+    stats_.AddLostPacket(i & 0xFFFF);
+  }
+  for (int i = 1000; i < 1500; i += 2) {
+    stats_.AddLostPacket(i);
+  }
+  EXPECT_EQ(500, stats_.GetSingleLossCount());
+  EXPECT_EQ(1, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(10, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets out of order and ensure that they still get counted
+// correctly as single or multiple loss events.
+TEST_F(PacketLossStatsTest, OutOfOrder) {
+  for (int i = 0; i < 1000; i += 10) {
+    stats_.AddLostPacket(i + 5);
+    stats_.AddLostPacket(i + 7);
+    stats_.AddLostPacket(i + 4);
+    stats_.AddLostPacket(i + 1);
+    stats_.AddLostPacket(i + 2);
+  }
+  EXPECT_EQ(100, stats_.GetSingleLossCount());
+  EXPECT_EQ(200, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(400, stats_.GetMultipleLossPacketCount());
+}
+
+// Add loss packets out of order and ensure that they still get counted
+// correctly as single or multiple loss events, and wrap in the middle of
+// adding.
+TEST_F(PacketLossStatsTest, OutOfOrderWrapped) {
+  for (int i = 65000; i < 66000; i += 10) {
+    stats_.AddLostPacket((i + 5) & 0xFFFF);
+    stats_.AddLostPacket((i + 7) & 0xFFFF);
+    stats_.AddLostPacket((i + 4) & 0xFFFF);
+    stats_.AddLostPacket((i + 1) & 0xFFFF);
+    stats_.AddLostPacket((i + 2) & 0xFFFF);
+  }
+  EXPECT_EQ(100, stats_.GetSingleLossCount());
+  EXPECT_EQ(200, stats_.GetMultipleLossEventCount());
+  EXPECT_EQ(400, stats_.GetMultipleLossPacketCount());
+}
+
+}  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index d2e224d..c46664f 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -604,6 +604,31 @@
   rtp_sender_.GetDataCounters(rtp_counters, rtx_counters);
 }
 
+void ModuleRtpRtcpImpl::GetRtpPacketLossStats(
+    bool outgoing,
+    uint32_t ssrc,
+    struct RtpPacketLossStats* loss_stats) const {
+  if (!loss_stats) return;
+  const PacketLossStats* stats_source = NULL;
+  if (outgoing) {
+    if (SSRC() == ssrc) {
+      stats_source = &send_loss_stats_;
+    }
+  } else {
+    if (rtcp_receiver_.RemoteSSRC() == ssrc) {
+      stats_source = &receive_loss_stats_;
+    }
+  }
+  if (stats_source) {
+    loss_stats->single_packet_loss_count =
+        stats_source->GetSingleLossCount();
+    loss_stats->multiple_packet_loss_event_count =
+        stats_source->GetMultipleLossEventCount();
+    loss_stats->multiple_packet_loss_packet_count =
+        stats_source->GetMultipleLossPacketCount();
+  }
+}
+
 int32_t ModuleRtpRtcpImpl::RemoteRTCPStat(RTCPSenderInfo* sender_info) {
   return rtcp_receiver_.SenderInfoReceived(sender_info);
 }
@@ -677,6 +702,9 @@
 // Send a Negative acknowledgment packet.
 int32_t ModuleRtpRtcpImpl::SendNACK(const uint16_t* nack_list,
                                     const uint16_t size) {
+  for (int i = 0; i < size; ++i) {
+    receive_loss_stats_.AddLostPacket(nack_list[i]);
+  }
   uint16_t nack_length = size;
   uint16_t start_id = 0;
   int64_t now = clock_->TimeInMilliseconds();
@@ -892,6 +920,9 @@
 
 void ModuleRtpRtcpImpl::OnReceivedNACK(
     const std::list<uint16_t>& nack_sequence_numbers) {
+  for (uint16_t nack_sequence_number : nack_sequence_numbers) {
+    send_loss_stats_.AddLostPacket(nack_sequence_number);
+  }
   if (!rtp_sender_.StorePackets() ||
       nack_sequence_numbers.size() == 0) {
     return;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 9cd7e70..791495a 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -16,6 +16,7 @@
 
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
+#include "webrtc/modules/rtp_rtcp/source/packet_loss_stats.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_receiver.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_sender.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
@@ -170,6 +171,11 @@
       StreamDataCounters* rtp_counters,
       StreamDataCounters* rtx_counters) const override;
 
+  void GetRtpPacketLossStats(
+      bool outgoing,
+      uint32_t ssrc,
+      struct RtpPacketLossStats* loss_stats) const override;
+
   // Get received RTCP report, sender info.
   int32_t RemoteRTCPStat(RTCPSenderInfo* sender_info) override;
 
@@ -374,6 +380,9 @@
 
   RtcpRttStats* rtt_stats_;
 
+  PacketLossStats send_loss_stats_;
+  PacketLossStats receive_loss_stats_;
+
   // The processed RTT from RtcpRttStats.
   rtc::scoped_ptr<CriticalSectionWrapper> critical_section_rtt_;
   int64_t rtt_ms_;