diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index af4c97bb70..7f3a1e809e 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 0eda287a4c..ca4b812731 100644 --- a/webrtc/modules/rtp_rtcp/BUILD.gn +++ b/webrtc/modules/rtp_rtcp/BUILD.gn @@ -35,6 +35,8 @@ source_set("rtp_rtcp") { "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 98f7c26b55..7a960881c9 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -430,6 +430,14 @@ class RtpRtcp : public Module { StreamDataCounters* rtp_counters, 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 * diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index ed7dfe06a0..ea366878c5 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 @@ class NullRtpAudioFeedback : public RtpAudioFeedback { 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 99e5b1c15a..127a795e0b 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 @@ class MockRtpRtcp : public RtpRtcp { 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 e73b43adfb..dcd47dfd72 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 0000000000..4ab3864086 --- /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 + +#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(*(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*> 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 0000000000..2eab043c0d --- /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 +#include + +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 lost_packets_buffer_; + std::set 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 0000000000..660628242d --- /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 d2e224dfa4..c46664f80d 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 @@ void ModuleRtpRtcpImpl::GetSendStreamDataCounters( 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 @@ int ModuleRtpRtcpImpl::SetSelectiveRetransmissions(uint8_t settings) { // 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 @@ bool ModuleRtpRtcpImpl::SendTimeOfXrRrReport( void ModuleRtpRtcpImpl::OnReceivedNACK( const std::list& 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 9cd7e70263..791495a8ba 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 @@ class ModuleRtpRtcpImpl : public RtpRtcp { 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 @@ class ModuleRtpRtcpImpl : public RtpRtcp { RtcpRttStats* rtt_stats_; + PacketLossStats send_loss_stats_; + PacketLossStats receive_loss_stats_; + // The processed RTT from RtcpRttStats. rtc::scoped_ptr critical_section_rtt_; int64_t rtt_ms_;