/* * 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 "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/modules/rtp_rtcp/include/receive_statistics.h" #include "webrtc/system_wrappers/include/clock.h" namespace webrtc { const size_t kPacketSize1 = 100; const size_t kPacketSize2 = 300; const uint32_t kSsrc1 = 1; const uint32_t kSsrc2 = 2; class ReceiveStatisticsTest : public ::testing::Test { public: ReceiveStatisticsTest() : clock_(0), receive_statistics_(ReceiveStatistics::Create(&clock_)) { memset(&header1_, 0, sizeof(header1_)); header1_.ssrc = kSsrc1; header1_.sequenceNumber = 100; memset(&header2_, 0, sizeof(header2_)); header2_.ssrc = kSsrc2; header2_.sequenceNumber = 100; } protected: SimulatedClock clock_; rtc::scoped_ptr 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); StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians(); EXPECT_EQ(2u, statisticians.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; statisticians[kSsrc1]->GetDataCounters(&bytes_received, &packets_received); EXPECT_EQ(300u, bytes_received); EXPECT_EQ(3u, packets_received); statisticians[kSsrc2]->GetDataCounters(&bytes_received, &packets_received); EXPECT_EQ(900u, bytes_received); EXPECT_EQ(3u, packets_received); 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, ActiveStatisticians) { receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; clock_.AdvanceTimeMilliseconds(1000); receive_statistics_->IncomingPacket(header2_, kPacketSize2, false); ++header2_.sequenceNumber; StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians(); // Nothing should time out since only 1000 ms has passed since the first // packet came in. EXPECT_EQ(2u, statisticians.size()); clock_.AdvanceTimeMilliseconds(7000); // kSsrc1 should have timed out. statisticians = receive_statistics_->GetActiveStatisticians(); EXPECT_EQ(1u, statisticians.size()); clock_.AdvanceTimeMilliseconds(1000); // kSsrc2 should have timed out. statisticians = receive_statistics_->GetActiveStatisticians(); EXPECT_EQ(0u, statisticians.size()); receive_statistics_->IncomingPacket(header1_, kPacketSize1, false); ++header1_.sequenceNumber; // kSsrc1 should be active again and the data counters should have survived. statisticians = receive_statistics_->GetActiveStatisticians(); EXPECT_EQ(1u, statisticians.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_() {} virtual ~TestCallback() {} virtual void StatisticsUpdated(const RtcpStatistics& statistics, uint32_t ssrc) { 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.cumulative_lost, callback.stats_.cumulative_lost); EXPECT_EQ(statistics.extended_max_sequence_number, 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); // 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_() {} virtual ~RtpTestCallback() {} virtual void DataCountersUpdated(const StreamDataCounters& counters, uint32_t ssrc) { 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 webrtc