| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/memory/scoped_ptr.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "media/cast/cast_environment.h" |
| #include "media/cast/net/cast_transport_defines.h" |
| #include "media/cast/net/rtcp/rtcp_utility.h" |
| #include "media/cast/net/rtcp/test_rtcp_packet_builder.h" |
| #include "media/cast/test/fake_single_thread_task_runner.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| namespace cast { |
| |
| static const uint32_t kSenderSsrc = 0x10203; |
| static const uint32_t kSourceSsrc = 0x40506; |
| static const uint32_t kUnknownSsrc = 0xDEAD; |
| static const base::TimeDelta kTargetDelay = |
| base::TimeDelta::FromMilliseconds(100); |
| |
| class RtcpParserTest : public ::testing::Test { |
| protected: |
| RtcpParserTest() |
| : testing_clock_(new base::SimpleTestTickClock()), |
| task_runner_(new test::FakeSingleThreadTaskRunner( |
| testing_clock_.get())) { |
| } |
| |
| bool HasAnything(const RtcpParser& parser) { |
| return parser.has_sender_report() || |
| parser.has_last_report() || |
| parser.has_receiver_log() || |
| parser.has_cast_message() || |
| parser.has_receiver_reference_time_report(); |
| } |
| |
| void ExpectSenderInfo(const RtcpParser& parser) { |
| EXPECT_TRUE(parser.has_sender_report()); |
| EXPECT_EQ(kNtpHigh, parser.sender_report().ntp_seconds); |
| EXPECT_EQ(kNtpLow, parser.sender_report().ntp_fraction); |
| EXPECT_EQ(kRtpTimestamp, parser.sender_report().rtp_timestamp); |
| EXPECT_EQ(kSendPacketCount, parser.sender_report().send_packet_count); |
| EXPECT_EQ(kSendOctetCount, parser.sender_report().send_octet_count); |
| } |
| |
| void ExpectLastReport(const RtcpParser& parser) { |
| EXPECT_TRUE(parser.has_last_report()); |
| EXPECT_EQ(kLastSr, parser.last_report()); |
| EXPECT_EQ(kDelayLastSr, parser.delay_since_last_report()); |
| } |
| |
| void ExpectReceiverReference(const RtcpParser& parser) { |
| EXPECT_TRUE(parser.has_receiver_reference_time_report()); |
| EXPECT_EQ(kSenderSsrc, parser.receiver_reference_time_report().remote_ssrc); |
| EXPECT_EQ(kNtpHigh, parser.receiver_reference_time_report().ntp_seconds); |
| EXPECT_EQ(kNtpLow, parser.receiver_reference_time_report().ntp_fraction); |
| } |
| |
| void ExpectCastFeedback(const RtcpParser& parser) { |
| EXPECT_TRUE(parser.has_cast_message()); |
| EXPECT_EQ(kSenderSsrc, parser.cast_message().media_ssrc); |
| EXPECT_EQ(kAckFrameId, parser.cast_message().ack_frame_id); |
| |
| MissingFramesAndPacketsMap::const_iterator frame_it = |
| parser.cast_message().missing_frames_and_packets.begin(); |
| |
| EXPECT_TRUE( |
| frame_it != parser.cast_message().missing_frames_and_packets.end()); |
| EXPECT_EQ(kLostFrameId, frame_it->first); |
| EXPECT_EQ(frame_it->second.size(), 1UL); |
| EXPECT_EQ(*frame_it->second.begin(), kRtcpCastAllPacketsLost); |
| ++frame_it; |
| EXPECT_TRUE( |
| frame_it != parser.cast_message().missing_frames_and_packets.end()); |
| EXPECT_EQ(kFrameIdWithLostPackets, frame_it->first); |
| EXPECT_EQ(3UL, frame_it->second.size()); |
| PacketIdSet::const_iterator packet_it = frame_it->second.begin(); |
| EXPECT_EQ(kLostPacketId1, *packet_it); |
| ++packet_it; |
| EXPECT_EQ(kLostPacketId2, *packet_it); |
| ++packet_it; |
| EXPECT_EQ(kLostPacketId3, *packet_it); |
| ++frame_it; |
| EXPECT_TRUE( |
| frame_it == parser.cast_message().missing_frames_and_packets.end()); |
| } |
| |
| void ExpectReceiverLog(const RtcpParser& parser, |
| const RtcpReceiverLogMessage& expected_receiver_log) { |
| EXPECT_TRUE(parser.has_receiver_log()); |
| EXPECT_EQ(expected_receiver_log.size(), parser.receiver_log().size()); |
| RtcpReceiverLogMessage::const_iterator expected_it = |
| expected_receiver_log.begin(); |
| RtcpReceiverLogMessage::const_iterator incoming_it = |
| parser.receiver_log().begin(); |
| for (; incoming_it != parser.receiver_log().end(); |
| ++incoming_it, ++expected_it) { |
| EXPECT_EQ(expected_it->rtp_timestamp_, incoming_it->rtp_timestamp_); |
| EXPECT_EQ(expected_it->event_log_messages_.size(), |
| incoming_it->event_log_messages_.size()); |
| |
| RtcpReceiverEventLogMessages::const_iterator event_incoming_it = |
| incoming_it->event_log_messages_.begin(); |
| RtcpReceiverEventLogMessages::const_iterator event_expected_it = |
| expected_it->event_log_messages_.begin(); |
| for (; event_incoming_it != incoming_it->event_log_messages_.end(); |
| ++event_incoming_it, ++event_expected_it) { |
| EXPECT_EQ(event_expected_it->type, event_incoming_it->type); |
| EXPECT_EQ(event_expected_it->event_timestamp, |
| event_incoming_it->event_timestamp); |
| if (event_expected_it->type == PACKET_RECEIVED) { |
| EXPECT_EQ(event_expected_it->packet_id, event_incoming_it->packet_id); |
| } else { |
| EXPECT_EQ(event_expected_it->delay_delta, |
| event_incoming_it->delay_delta); |
| } |
| } |
| } |
| } |
| |
| scoped_ptr<base::SimpleTestTickClock> testing_clock_; |
| scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RtcpParserTest); |
| }; |
| |
| TEST_F(RtcpParserTest, BrokenPacketIsIgnored) { |
| const char bad_packet[] = {0, 0, 0, 0}; |
| RtcpParser parser(kSourceSsrc, kSenderSsrc); |
| base::BigEndianReader reader(bad_packet, sizeof(bad_packet)); |
| EXPECT_FALSE(parser.Parse(&reader)); |
| } |
| |
| TEST_F(RtcpParserTest, UnknownBlockIgnored) { |
| // Only unknown data, nothing happens. |
| TestRtcpPacketBuilder p; |
| p.AddUnknownBlock(); |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| // Add valid sender report *after* unknown data - should work fine. |
| p.AddSr(kSenderSsrc, 0); |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p.Reader())); |
| ExpectSenderInfo(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectSenderReportPacket) { |
| TestRtcpPacketBuilder p; |
| p.AddSr(kSenderSsrc, 0); |
| |
| // Expected to be ignored since the sender ssrc does not match our |
| // remote ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| // Expected to be pass through since the sender ssrc match our remote ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p.Reader())); |
| ExpectSenderInfo(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiveReportPacket) { |
| TestRtcpPacketBuilder p1; |
| p1.AddRr(kSenderSsrc, 1); |
| p1.AddRb(kUnknownSsrc); |
| |
| // Expected to be ignored since the source ssrc does not match our |
| // local ssrc. |
| RtcpParser parser1(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser1.Parse(p1.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| TestRtcpPacketBuilder p2; |
| p2.AddRr(kSenderSsrc, 1); |
| p2.AddRb(kSourceSsrc); |
| |
| // Expected to be pass through since the sender ssrc match our local ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p2.Reader())); |
| ExpectLastReport(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectSenderReportWithReportBlockPacket) { |
| TestRtcpPacketBuilder p1; |
| p1.AddSr(kSenderSsrc, 1); |
| p1.AddRb(kUnknownSsrc); |
| |
| // Sender report expected to be ignored since the sender ssrc does not match |
| // our remote ssrc. |
| // Report block expected to be ignored since the source ssrc does not match |
| // our local ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p1.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| // Sender report expected to be pass through since the sender ssrc match our |
| // remote ssrc. |
| // Report block expected to be ignored since the source ssrc does not match |
| // our local ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p1.Reader())); |
| ExpectSenderInfo(parser2); |
| EXPECT_FALSE(parser2.has_last_report()); |
| |
| // Sender report expected to be ignored since the sender ssrc does not match |
| // our remote ssrc. |
| // Report block expected to be ignored too since it's a part of the |
| // sender report. |
| TestRtcpPacketBuilder p2; |
| p2.AddSr(kSenderSsrc, 1); |
| p2.AddRb(kSourceSsrc); |
| |
| RtcpParser parser3(kSourceSsrc, 0); |
| EXPECT_TRUE(parser3.Parse(p2.Reader())); |
| EXPECT_FALSE(parser3.has_last_report()); |
| |
| // Sender report expected to be pass through since the sender ssrc match our |
| // remote ssrc. |
| // Report block expected to be pass through since the sender ssrc match |
| // our local ssrc. |
| RtcpParser parser4(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser4.Parse(p2.Reader())); |
| ExpectSenderInfo(parser4); |
| ExpectLastReport(parser4); |
| } |
| |
| TEST_F(RtcpParserTest, InjectSenderReportPacketWithDlrr) { |
| TestRtcpPacketBuilder p; |
| p.AddSr(kSenderSsrc, 0); |
| p.AddXrHeader(kSenderSsrc); |
| p.AddXrUnknownBlock(); |
| p.AddXrExtendedDlrrBlock(kSenderSsrc); |
| p.AddXrUnknownBlock(); |
| |
| // Expected to be ignored since the source ssrc does not match our |
| // local ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| // Expected to be pass through since the sender ssrc match our local ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p.Reader())); |
| ExpectSenderInfo(parser2); |
| // DLRRs are ignored. |
| EXPECT_FALSE(parser2.has_last_report()); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiverReportPacketWithRrtr) { |
| TestRtcpPacketBuilder p1; |
| p1.AddRr(kSenderSsrc, 1); |
| p1.AddRb(kUnknownSsrc); |
| p1.AddXrHeader(kSenderSsrc); |
| p1.AddXrRrtrBlock(); |
| |
| // Expected to be ignored since the source ssrc does not match our |
| // local ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p1.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| TestRtcpPacketBuilder p2; |
| p2.AddRr(kSenderSsrc, 1); |
| p2.AddRb(kSourceSsrc); |
| p2.AddXrHeader(kSenderSsrc); |
| p2.AddXrRrtrBlock(); |
| |
| // Expected to be pass through since the sender ssrc match our local ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p2.Reader())); |
| ExpectLastReport(parser2); |
| ExpectReceiverReference(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiverReportPacketWithIntraFrameRequest) { |
| TestRtcpPacketBuilder p1; |
| p1.AddRr(kSenderSsrc, 1); |
| p1.AddRb(kUnknownSsrc); |
| |
| // Expected to be ignored since the source ssrc does not match our |
| // local ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p1.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| TestRtcpPacketBuilder p2; |
| p2.AddRr(kSenderSsrc, 1); |
| p2.AddRb(kSourceSsrc); |
| |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p2.Reader())); |
| ExpectLastReport(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiverReportPacketWithCastFeedback) { |
| TestRtcpPacketBuilder p1; |
| p1.AddRr(kSenderSsrc, 1); |
| p1.AddRb(kUnknownSsrc); |
| p1.AddCast(kSenderSsrc, kUnknownSsrc, kTargetDelay); |
| |
| // Expected to be ignored since the source ssrc does not match our |
| // local ssrc. |
| RtcpParser parser1(kSourceSsrc, 0); |
| EXPECT_TRUE(parser1.Parse(p1.Reader())); |
| EXPECT_FALSE(HasAnything(parser1)); |
| |
| TestRtcpPacketBuilder p2; |
| p2.AddRr(kSenderSsrc, 1); |
| p2.AddRb(kSourceSsrc); |
| p2.AddCast(kSenderSsrc, kSourceSsrc, kTargetDelay); |
| |
| // Expected to be pass through since the sender ssrc match our local ssrc. |
| RtcpParser parser2(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser2.Parse(p2.Reader())); |
| ExpectLastReport(parser2); |
| ExpectCastFeedback(parser2); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationBase) { |
| static const uint32_t kTimeBaseMs = 12345678; |
| static const uint32_t kTimeDelayMs = 10; |
| static const uint32_t kDelayDeltaMs = 123; |
| base::SimpleTestTickClock testing_clock; |
| testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs)); |
| |
| RtcpReceiverLogMessage receiver_log; |
| RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp); |
| RtcpReceiverEventLogMessage event_log; |
| |
| event_log.type = FRAME_ACK_SENT; |
| event_log.event_timestamp = testing_clock.NowTicks(); |
| event_log.delay_delta = base::TimeDelta::FromMilliseconds(kDelayDeltaMs); |
| frame_log.event_log_messages_.push_back(event_log); |
| |
| testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs)); |
| event_log.type = PACKET_RECEIVED; |
| event_log.event_timestamp = testing_clock.NowTicks(); |
| event_log.packet_id = kLostPacketId1; |
| frame_log.event_log_messages_.push_back(event_log); |
| |
| event_log.type = PACKET_RECEIVED; |
| event_log.event_timestamp = testing_clock.NowTicks(); |
| event_log.packet_id = kLostPacketId2; |
| frame_log.event_log_messages_.push_back(event_log); |
| |
| receiver_log.push_back(frame_log); |
| |
| TestRtcpPacketBuilder p; |
| p.AddRr(kSenderSsrc, 1); |
| p.AddRb(kSourceSsrc); |
| p.AddReceiverLog(kSenderSsrc); |
| p.AddReceiverFrameLog(kRtpTimestamp, 3, kTimeBaseMs); |
| p.AddReceiverEventLog(kDelayDeltaMs, FRAME_ACK_SENT, 0); |
| p.AddReceiverEventLog(kLostPacketId1, PACKET_RECEIVED, kTimeDelayMs); |
| p.AddReceiverEventLog(kLostPacketId2, PACKET_RECEIVED, kTimeDelayMs); |
| |
| RtcpParser parser(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser.Parse(p.Reader())); |
| ExpectReceiverLog(parser, receiver_log); |
| } |
| |
| TEST_F(RtcpParserTest, InjectReceiverReportWithReceiverLogVerificationMulti) { |
| static const uint32_t kTimeBaseMs = 12345678; |
| static const uint32_t kTimeDelayMs = 10; |
| static const int kDelayDeltaMs = 123; // To be varied for every frame. |
| base::SimpleTestTickClock testing_clock; |
| testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeBaseMs)); |
| |
| RtcpReceiverLogMessage receiver_log; |
| |
| for (int j = 0; j < 100; ++j) { |
| RtcpReceiverFrameLogMessage frame_log(kRtpTimestamp); |
| RtcpReceiverEventLogMessage event_log; |
| event_log.type = FRAME_ACK_SENT; |
| event_log.event_timestamp = testing_clock.NowTicks(); |
| event_log.delay_delta = |
| base::TimeDelta::FromMilliseconds((j - 50) * kDelayDeltaMs); |
| frame_log.event_log_messages_.push_back(event_log); |
| receiver_log.push_back(frame_log); |
| testing_clock.Advance(base::TimeDelta::FromMilliseconds(kTimeDelayMs)); |
| } |
| |
| TestRtcpPacketBuilder p; |
| p.AddRr(kSenderSsrc, 1); |
| p.AddRb(kSourceSsrc); |
| p.AddReceiverLog(kSenderSsrc); |
| for (int i = 0; i < 100; ++i) { |
| p.AddReceiverFrameLog(kRtpTimestamp, 1, kTimeBaseMs + i * kTimeDelayMs); |
| const int delay = (i - 50) * kDelayDeltaMs; |
| p.AddReceiverEventLog(static_cast<uint16_t>(delay), FRAME_ACK_SENT, 0); |
| } |
| |
| RtcpParser parser(kSourceSsrc, kSenderSsrc); |
| EXPECT_TRUE(parser.Parse(p.Reader())); |
| ExpectReceiverLog(parser, receiver_log); |
| } |
| |
| } // namespace cast |
| } // namespace media |