blob: 29fc88daac5226132895c812801666c3ef089b2c [file] [log] [blame]
/*
* Copyright (c) 2013 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 <memory>
#include <vector>
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
const size_t kPacketSize1 = 100;
const size_t kPacketSize2 = 300;
const uint32_t kSsrc1 = 101;
const uint32_t kSsrc2 = 202;
const uint32_t kSsrc3 = 203;
const uint32_t kSsrc4 = 304;
RTPHeader CreateRtpHeader(uint32_t ssrc) {
RTPHeader header;
memset(&header, 0, sizeof(header));
header.ssrc = ssrc;
header.sequenceNumber = 100;
return header;
}
class ReceiveStatisticsTest : public ::testing::Test {
public:
ReceiveStatisticsTest()
: clock_(0), receive_statistics_(ReceiveStatistics::Create(&clock_)) {
header1_ = CreateRtpHeader(kSsrc1);
header2_ = CreateRtpHeader(kSsrc2);
}
protected:
SimulatedClock clock_;
std::unique_ptr<ReceiveStatistics> receive_statistics_;
RTPHeader header1_;
RTPHeader header2_;
};
TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
++header2_.sequenceNumber;
clock_.AdvanceTimeMilliseconds(100);
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
++header2_.sequenceNumber;
StreamStatistician* statistician =
receive_statistics_->GetStatistician(kSsrc1);
ASSERT_TRUE(statistician != NULL);
EXPECT_GT(statistician->BitrateReceived(), 0u);
size_t bytes_received = 0;
uint32_t packets_received = 0;
statistician->GetDataCounters(&bytes_received, &packets_received);
EXPECT_EQ(200u, bytes_received);
EXPECT_EQ(2u, packets_received);
statistician = receive_statistics_->GetStatistician(kSsrc2);
ASSERT_TRUE(statistician != NULL);
EXPECT_GT(statistician->BitrateReceived(), 0u);
statistician->GetDataCounters(&bytes_received, &packets_received);
EXPECT_EQ(600u, bytes_received);
EXPECT_EQ(2u, packets_received);
EXPECT_EQ(2u, receive_statistics_->RtcpReportBlocks(3).size());
// Add more incoming packets and verify that they are registered in both
// access methods.
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
++header2_.sequenceNumber;
receive_statistics_->GetStatistician(kSsrc1)->GetDataCounters(
&bytes_received, &packets_received);
EXPECT_EQ(300u, bytes_received);
EXPECT_EQ(3u, packets_received);
receive_statistics_->GetStatistician(kSsrc2)->GetDataCounters(
&bytes_received, &packets_received);
EXPECT_EQ(900u, bytes_received);
EXPECT_EQ(3u, packets_received);
}
TEST_F(ReceiveStatisticsTest,
RtcpReportBlocksReturnsMaxBlocksWhenThereAreMoreStatisticians) {
RTPHeader header1 = CreateRtpHeader(kSsrc1);
RTPHeader header2 = CreateRtpHeader(kSsrc2);
RTPHeader header3 = CreateRtpHeader(kSsrc3);
receive_statistics_->IncomingPacket(header1, kPacketSize1, false);
receive_statistics_->IncomingPacket(header2, kPacketSize1, false);
receive_statistics_->IncomingPacket(header3, kPacketSize1, false);
EXPECT_THAT(receive_statistics_->RtcpReportBlocks(2), SizeIs(2));
EXPECT_THAT(receive_statistics_->RtcpReportBlocks(2), SizeIs(2));
EXPECT_THAT(receive_statistics_->RtcpReportBlocks(2), SizeIs(2));
}
TEST_F(ReceiveStatisticsTest,
RtcpReportBlocksReturnsAllObservedSsrcsWithMultipleCalls) {
RTPHeader header1 = CreateRtpHeader(kSsrc1);
RTPHeader header2 = CreateRtpHeader(kSsrc2);
RTPHeader header3 = CreateRtpHeader(kSsrc3);
RTPHeader header4 = CreateRtpHeader(kSsrc4);
receive_statistics_->IncomingPacket(header1, kPacketSize1, false);
receive_statistics_->IncomingPacket(header2, kPacketSize1, false);
receive_statistics_->IncomingPacket(header3, kPacketSize1, false);
receive_statistics_->IncomingPacket(header4, kPacketSize1, false);
std::vector<uint32_t> observed_ssrcs;
std::vector<rtcp::ReportBlock> report_blocks =
receive_statistics_->RtcpReportBlocks(2);
ASSERT_THAT(report_blocks, SizeIs(2));
observed_ssrcs.push_back(report_blocks[0].source_ssrc());
observed_ssrcs.push_back(report_blocks[1].source_ssrc());
report_blocks = receive_statistics_->RtcpReportBlocks(2);
ASSERT_THAT(report_blocks, SizeIs(2));
observed_ssrcs.push_back(report_blocks[0].source_ssrc());
observed_ssrcs.push_back(report_blocks[1].source_ssrc());
EXPECT_THAT(observed_ssrcs,
UnorderedElementsAre(kSsrc1, kSsrc2, kSsrc3, kSsrc4));
}
TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
clock_.AdvanceTimeMilliseconds(1000);
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
++header2_.sequenceNumber;
// Nothing should time out since only 1000 ms has passed since the first
// packet came in.
EXPECT_EQ(2u, receive_statistics_->RtcpReportBlocks(3).size());
clock_.AdvanceTimeMilliseconds(7000);
// kSsrc1 should have timed out.
EXPECT_EQ(1u, receive_statistics_->RtcpReportBlocks(3).size());
clock_.AdvanceTimeMilliseconds(1000);
// kSsrc2 should have timed out.
EXPECT_EQ(0u, receive_statistics_->RtcpReportBlocks(3).size());
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
// kSsrc1 should be active again and the data counters should have survived.
EXPECT_EQ(1u, receive_statistics_->RtcpReportBlocks(3).size());
StreamStatistician* statistician =
receive_statistics_->GetStatistician(kSsrc1);
ASSERT_TRUE(statistician != NULL);
size_t bytes_received = 0;
uint32_t packets_received = 0;
statistician->GetDataCounters(&bytes_received, &packets_received);
EXPECT_EQ(200u, bytes_received);
EXPECT_EQ(2u, packets_received);
}
TEST_F(ReceiveStatisticsTest, GetReceiveStreamDataCounters) {
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
StreamStatistician* statistician =
receive_statistics_->GetStatistician(kSsrc1);
ASSERT_TRUE(statistician != NULL);
StreamDataCounters counters;
statistician->GetReceiveStreamDataCounters(&counters);
EXPECT_GT(counters.first_packet_time_ms, -1);
EXPECT_EQ(1u, counters.transmitted.packets);
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
statistician->GetReceiveStreamDataCounters(&counters);
EXPECT_GT(counters.first_packet_time_ms, -1);
EXPECT_EQ(2u, counters.transmitted.packets);
}
TEST_F(ReceiveStatisticsTest, RtcpCallbacks) {
class TestCallback : public RtcpStatisticsCallback {
public:
TestCallback()
: RtcpStatisticsCallback(), num_calls_(0), ssrc_(0), stats_() {}
~TestCallback() override {}
void StatisticsUpdated(const RtcpStatistics& statistics,
uint32_t ssrc) override {
ssrc_ = ssrc;
stats_ = statistics;
++num_calls_;
}
void CNameChanged(const char* cname, uint32_t ssrc) override {}
uint32_t num_calls_;
uint32_t ssrc_;
RtcpStatistics stats_;
} callback;
receive_statistics_->RegisterRtcpStatisticsCallback(&callback);
// Add some arbitrary data, with loss and jitter.
header1_.sequenceNumber = 1;
clock_.AdvanceTimeMilliseconds(7);
header1_.timestamp += 3;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
header1_.sequenceNumber += 2;
clock_.AdvanceTimeMilliseconds(9);
header1_.timestamp += 9;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
--header1_.sequenceNumber;
clock_.AdvanceTimeMilliseconds(13);
header1_.timestamp += 47;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
header1_.sequenceNumber += 3;
clock_.AdvanceTimeMilliseconds(11);
header1_.timestamp += 17;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
EXPECT_EQ(0u, callback.num_calls_);
// Call GetStatistics, simulating a timed rtcp sender thread.
RtcpStatistics statistics;
receive_statistics_->GetStatistician(kSsrc1)->GetStatistics(&statistics,
true);
EXPECT_EQ(1u, callback.num_calls_);
EXPECT_EQ(callback.ssrc_, kSsrc1);
EXPECT_EQ(statistics.packets_lost, callback.stats_.packets_lost);
EXPECT_EQ(statistics.extended_highest_sequence_number,
callback.stats_.extended_highest_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(1, statistics.packets_lost);
EXPECT_EQ(5u, statistics.extended_highest_sequence_number);
EXPECT_EQ(4u, statistics.jitter);
receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
// Add some more data.
header1_.sequenceNumber = 1;
clock_.AdvanceTimeMilliseconds(7);
header1_.timestamp += 3;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
header1_.sequenceNumber += 2;
clock_.AdvanceTimeMilliseconds(9);
header1_.timestamp += 9;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
--header1_.sequenceNumber;
clock_.AdvanceTimeMilliseconds(13);
header1_.timestamp += 47;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, true);
header1_.sequenceNumber += 3;
clock_.AdvanceTimeMilliseconds(11);
header1_.timestamp += 17;
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
++header1_.sequenceNumber;
receive_statistics_->GetStatistician(kSsrc1)->GetStatistics(&statistics,
true);
// Should not have been called after deregister.
EXPECT_EQ(1u, callback.num_calls_);
}
class RtpTestCallback : public StreamDataCountersCallback {
public:
RtpTestCallback()
: StreamDataCountersCallback(), num_calls_(0), ssrc_(0), stats_() {}
~RtpTestCallback() override = default;
void DataCountersUpdated(const StreamDataCounters& counters,
uint32_t ssrc) override {
ssrc_ = ssrc;
stats_ = counters;
++num_calls_;
}
void MatchPacketCounter(const RtpPacketCounter& expected,
const RtpPacketCounter& actual) {
EXPECT_EQ(expected.payload_bytes, actual.payload_bytes);
EXPECT_EQ(expected.header_bytes, actual.header_bytes);
EXPECT_EQ(expected.padding_bytes, actual.padding_bytes);
EXPECT_EQ(expected.packets, actual.packets);
}
void Matches(uint32_t num_calls,
uint32_t ssrc,
const StreamDataCounters& expected) {
EXPECT_EQ(num_calls, num_calls_);
EXPECT_EQ(ssrc, ssrc_);
MatchPacketCounter(expected.transmitted, stats_.transmitted);
MatchPacketCounter(expected.retransmitted, stats_.retransmitted);
MatchPacketCounter(expected.fec, stats_.fec);
}
uint32_t num_calls_;
uint32_t ssrc_;
StreamDataCounters stats_;
};
TEST_F(ReceiveStatisticsTest, RtpCallbacks) {
RtpTestCallback callback;
receive_statistics_->RegisterRtpStatisticsCallback(&callback);
const size_t kHeaderLength = 20;
const size_t kPaddingLength = 9;
// One packet of size kPacketSize1.
header1_.headerLength = kHeaderLength;
receive_statistics_->IncomingPacket(header1_, kPacketSize1 + kHeaderLength,
false);
StreamDataCounters expected;
expected.transmitted.payload_bytes = kPacketSize1;
expected.transmitted.header_bytes = kHeaderLength;
expected.transmitted.padding_bytes = 0;
expected.transmitted.packets = 1;
expected.retransmitted.payload_bytes = 0;
expected.retransmitted.header_bytes = 0;
expected.retransmitted.padding_bytes = 0;
expected.retransmitted.packets = 0;
expected.fec.packets = 0;
callback.Matches(1, kSsrc1, expected);
++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);
expected.transmitted.payload_bytes = kPacketSize1 * 2;
expected.transmitted.header_bytes = kHeaderLength * 2;
expected.transmitted.padding_bytes = kPaddingLength;
expected.transmitted.packets = 2;
callback.Matches(2, kSsrc1, expected);
clock_.AdvanceTimeMilliseconds(5);
// Retransmit last packet.
receive_statistics_->IncomingPacket(
header1_, kPacketSize1 + kHeaderLength + kPaddingLength, true);
expected.transmitted.payload_bytes = kPacketSize1 * 3;
expected.transmitted.header_bytes = kHeaderLength * 3;
expected.transmitted.padding_bytes = kPaddingLength * 2;
expected.transmitted.packets = 3;
expected.retransmitted.payload_bytes = kPacketSize1;
expected.retransmitted.header_bytes = kHeaderLength;
expected.retransmitted.padding_bytes = kPaddingLength;
expected.retransmitted.packets = 1;
callback.Matches(3, kSsrc1, expected);
header1_.paddingLength = 0;
++header1_.sequenceNumber;
clock_.AdvanceTimeMilliseconds(5);
// One FEC packet.
receive_statistics_->IncomingPacket(header1_, kPacketSize1 + kHeaderLength,
false);
receive_statistics_->FecPacketReceived(header1_,
kPacketSize1 + kHeaderLength);
expected.transmitted.payload_bytes = kPacketSize1 * 4;
expected.transmitted.header_bytes = kHeaderLength * 4;
expected.transmitted.packets = 4;
expected.fec.payload_bytes = kPacketSize1;
expected.fec.header_bytes = kHeaderLength;
expected.fec.packets = 1;
callback.Matches(5, kSsrc1, expected);
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.Matches(5, kSsrc1, expected);
}
TEST_F(ReceiveStatisticsTest, RtpCallbacksFecFirst) {
RtpTestCallback callback;
receive_statistics_->RegisterRtpStatisticsCallback(&callback);
const uint32_t kHeaderLength = 20;
header1_.headerLength = kHeaderLength;
// If first packet is FEC, ignore it.
receive_statistics_->FecPacketReceived(header1_,
kPacketSize1 + kHeaderLength);
EXPECT_EQ(0u, callback.num_calls_);
receive_statistics_->IncomingPacket(header1_, kPacketSize1 + kHeaderLength,
false);
StreamDataCounters expected;
expected.transmitted.payload_bytes = kPacketSize1;
expected.transmitted.header_bytes = kHeaderLength;
expected.transmitted.padding_bytes = 0;
expected.transmitted.packets = 1;
expected.fec.packets = 0;
callback.Matches(1, kSsrc1, expected);
receive_statistics_->FecPacketReceived(header1_,
kPacketSize1 + kHeaderLength);
expected.fec.payload_bytes = kPacketSize1;
expected.fec.header_bytes = kHeaderLength;
expected.fec.packets = 1;
callback.Matches(2, kSsrc1, expected);
}
} // namespace
} // namespace webrtc