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}
This commit is contained in:
bcornell
2015-07-10 18:10:05 -07:00
committed by Commit bot
parent 35b72fbceb
commit 30409b4dca
11 changed files with 459 additions and 0 deletions

View File

@ -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',

View File

@ -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",

View File

@ -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
*

View File

@ -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_

View File

@ -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,

View File

@ -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',

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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<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;

View File

@ -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<CriticalSectionWrapper> critical_section_rtt_;
int64_t rtt_ms_;