/* * Copyright (c) 2017 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 #include "common_types.h" // NOLINT(build/include) #include "modules/rtp_rtcp/include/rtp_header_parser.h" #include "modules/rtp_rtcp/include/rtp_payload_registry.h" #include "modules/rtp_rtcp/include/rtp_receiver.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/mocks/mock_rtp_rtcp.h" #include "test/gmock.h" #include "test/gtest.h" namespace webrtc { namespace { using ::testing::NiceMock; using ::testing::UnorderedElementsAre; const uint32_t kTestRate = 64000u; const uint8_t kTestPayload[] = {'t', 'e', 's', 't'}; const uint8_t kPcmuPayloadType = 96; const int64_t kGetSourcesTimeoutMs = 10000; const uint32_t kSsrc1 = 123; const uint32_t kSsrc2 = 124; const uint32_t kCsrc1 = 111; const uint32_t kCsrc2 = 222; static uint32_t rtp_timestamp(int64_t time_ms) { return static_cast(time_ms * kTestRate / 1000); } } // namespace class RtpReceiverTest : public ::testing::Test { protected: RtpReceiverTest() : fake_clock_(123456), rtp_receiver_( RtpReceiver::CreateAudioReceiver(&fake_clock_, &mock_rtp_data_, &rtp_payload_registry_)) { rtp_receiver_->RegisterReceivePayload(kPcmuPayloadType, SdpAudioFormat("PCMU", 8000, 1)); } ~RtpReceiverTest() {} bool FindSourceByIdAndType(const std::vector& sources, uint32_t source_id, RtpSourceType type, RtpSource* source) { for (size_t i = 0; i < sources.size(); ++i) { if (sources[i].source_id() == source_id && sources[i].source_type() == type) { (*source) = sources[i]; return true; } } return false; } SimulatedClock fake_clock_; NiceMock mock_rtp_data_; RTPPayloadRegistry rtp_payload_registry_; std::unique_ptr rtp_receiver_; }; TEST_F(RtpReceiverTest, GetSources) { int64_t now_ms = fake_clock_.TimeInMilliseconds(); RTPHeader header; header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(now_ms); header.numCSRCs = 2; header.arrOfCSRCs[0] = kCsrc1; header.arrOfCSRCs[1] = kCsrc2; const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); auto sources = rtp_receiver_->GetSources(); // One SSRC source and two CSRC sources. EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC), RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC), RtpSource(now_ms, kCsrc2, RtpSourceType::CSRC))); // Advance the fake clock and the method is expected to return the // contributing source object with same source id and updated timestamp. fake_clock_.AdvanceTimeMilliseconds(1); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); now_ms = fake_clock_.TimeInMilliseconds(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC), RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC), RtpSource(now_ms, kCsrc2, RtpSourceType::CSRC))); // Test the edge case that the sources are still there just before the // timeout. int64_t prev_time_ms = fake_clock_.TimeInMilliseconds(); fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(prev_time_ms, kSsrc1, RtpSourceType::SSRC), RtpSource(prev_time_ms, kCsrc1, RtpSourceType::CSRC), RtpSource(prev_time_ms, kCsrc2, RtpSourceType::CSRC))); // Time out. fake_clock_.AdvanceTimeMilliseconds(1); sources = rtp_receiver_->GetSources(); // All the sources should be out of date. ASSERT_EQ(0u, sources.size()); } // Test the case that the SSRC is changed. TEST_F(RtpReceiverTest, GetSourcesChangeSSRC) { int64_t prev_time_ms = -1; int64_t now_ms = fake_clock_.TimeInMilliseconds(); RTPHeader header; header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(now_ms); const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); auto sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC))); // The SSRC is changed and the old SSRC is expected to be returned. fake_clock_.AdvanceTimeMilliseconds(100); prev_time_ms = now_ms; now_ms = fake_clock_.TimeInMilliseconds(); header.ssrc = kSsrc2; header.timestamp = rtp_timestamp(now_ms); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(prev_time_ms, kSsrc1, RtpSourceType::SSRC), RtpSource(now_ms, kSsrc2, RtpSourceType::SSRC))); // The SSRC is changed again and happen to be changed back to 1. No // duplication is expected. fake_clock_.AdvanceTimeMilliseconds(100); header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(now_ms); prev_time_ms = now_ms; now_ms = fake_clock_.TimeInMilliseconds(); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(prev_time_ms, kSsrc2, RtpSourceType::SSRC), RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC))); // Old SSRC source timeout. fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs); now_ms = fake_clock_.TimeInMilliseconds(); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC))); } TEST_F(RtpReceiverTest, GetSourcesRemoveOutdatedSource) { int64_t now_ms = fake_clock_.TimeInMilliseconds(); RTPHeader header; header.payloadType = kPcmuPayloadType; header.timestamp = rtp_timestamp(now_ms); const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; header.numCSRCs = 1; size_t kSourceListSize = 20; for (size_t i = 0; i < kSourceListSize; ++i) { header.ssrc = i; header.arrOfCSRCs[0] = (i + 1); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); } RtpSource source(0, 0, RtpSourceType::SSRC); auto sources = rtp_receiver_->GetSources(); // Expect |kSourceListSize| SSRC sources and |kSourceListSize| CSRC sources. ASSERT_EQ(2 * kSourceListSize, sources.size()); for (size_t i = 0; i < kSourceListSize; ++i) { // The SSRC source IDs are expected to be 19, 18, 17 ... 0 ASSERT_TRUE( FindSourceByIdAndType(sources, i, RtpSourceType::SSRC, &source)); EXPECT_EQ(now_ms, source.timestamp_ms()); // The CSRC source IDs are expected to be 20, 19, 18 ... 1 ASSERT_TRUE( FindSourceByIdAndType(sources, (i + 1), RtpSourceType::CSRC, &source)); EXPECT_EQ(now_ms, source.timestamp_ms()); } fake_clock_.AdvanceTimeMilliseconds(kGetSourcesTimeoutMs); for (size_t i = 0; i < kSourceListSize; ++i) { // The SSRC source IDs are expected to be 19, 18, 17 ... 0 ASSERT_TRUE( FindSourceByIdAndType(sources, i, RtpSourceType::SSRC, &source)); EXPECT_EQ(now_ms, source.timestamp_ms()); // The CSRC source IDs are expected to be 20, 19, 18 ... 1 ASSERT_TRUE( FindSourceByIdAndType(sources, (i + 1), RtpSourceType::CSRC, &source)); EXPECT_EQ(now_ms, source.timestamp_ms()); } // Timeout. All the existing objects are out of date and are expected to be // removed. fake_clock_.AdvanceTimeMilliseconds(1); header.ssrc = kSsrc1; header.arrOfCSRCs[0] = kCsrc1; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); now_ms = fake_clock_.TimeInMilliseconds(); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(now_ms, kSsrc1, RtpSourceType::SSRC), RtpSource(now_ms, kCsrc1, RtpSourceType::CSRC))); } // The audio level from the RTPHeader extension should be stored in the // RtpSource with the matching SSRC. TEST_F(RtpReceiverTest, GetSourcesContainsAudioLevelExtension) { RTPHeader header; int64_t time1_ms = fake_clock_.TimeInMilliseconds(); header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(time1_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 10; const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); auto sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre(RtpSource( time1_ms, kSsrc1, RtpSourceType::SSRC, 10))); // Receive a packet from a different SSRC with a different level and check // that they are both remembered. fake_clock_.AdvanceTimeMilliseconds(1); int64_t time2_ms = fake_clock_.TimeInMilliseconds(); header.ssrc = kSsrc2; header.timestamp = rtp_timestamp(time2_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 20; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(time1_ms, kSsrc1, RtpSourceType::SSRC, 10), RtpSource(time2_ms, kSsrc2, RtpSourceType::SSRC, 20))); // Receive a packet from the first SSRC again and check that the level is // updated. fake_clock_.AdvanceTimeMilliseconds(1); int64_t time3_ms = fake_clock_.TimeInMilliseconds(); header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(time3_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 30; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(time3_ms, kSsrc1, RtpSourceType::SSRC, 30), RtpSource(time2_ms, kSsrc2, RtpSourceType::SSRC, 20))); } TEST_F(RtpReceiverTest, MissingAudioLevelHeaderExtensionClearsRtpSourceAudioLevel) { RTPHeader header; int64_t time1_ms = fake_clock_.TimeInMilliseconds(); header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(time1_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 10; const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); auto sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre(RtpSource( time1_ms, kSsrc1, RtpSourceType::SSRC, 10))); // Receive a second packet without the audio level header extension and check // that the audio level is cleared. fake_clock_.AdvanceTimeMilliseconds(1); int64_t time2_ms = fake_clock_.TimeInMilliseconds(); header.timestamp = rtp_timestamp(time2_ms); header.extension.hasAudioLevel = false; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); sources = rtp_receiver_->GetSources(); EXPECT_THAT(sources, UnorderedElementsAre( RtpSource(time2_ms, kSsrc1, RtpSourceType::SSRC))); } TEST_F(RtpReceiverTest, UpdatesTimestampsIfAndOnlyIfPacketArrivesInOrder) { RTPHeader header; int64_t time1_ms = fake_clock_.TimeInMilliseconds(); header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(time1_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 10; header.sequenceNumber = 0xfff0; const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; uint32_t latest_timestamp; int64_t latest_receive_time_ms; // No packet received yet. EXPECT_FALSE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); // Initial packet const uint32_t timestamp_1 = header.timestamp; const int64_t receive_time_1 = fake_clock_.TimeInMilliseconds(); EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_1); EXPECT_EQ(latest_receive_time_ms, receive_time_1); // Late packet, timestamp not recorded. fake_clock_.AdvanceTimeMilliseconds(10); header.timestamp -= 900; header.sequenceNumber -= 2; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_1); EXPECT_EQ(latest_receive_time_ms, receive_time_1); // New packet, still late, no wraparound. fake_clock_.AdvanceTimeMilliseconds(10); header.timestamp += 1800; header.sequenceNumber += 1; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_1); EXPECT_EQ(latest_receive_time_ms, receive_time_1); // New packet, new timestamp recorded fake_clock_.AdvanceTimeMilliseconds(10); header.timestamp += 900; header.sequenceNumber += 2; const uint32_t timestamp_2 = header.timestamp; const int64_t receive_time_2 = fake_clock_.TimeInMilliseconds(); const uint16_t seqno_2 = header.sequenceNumber; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_2); EXPECT_EQ(latest_receive_time_ms, receive_time_2); // New packet, timestamp wraps around fake_clock_.AdvanceTimeMilliseconds(10); header.timestamp += 900; header.sequenceNumber += 20; const uint32_t timestamp_3 = header.timestamp; const int64_t receive_time_3 = fake_clock_.TimeInMilliseconds(); EXPECT_LT(header.sequenceNumber, seqno_2); // Wrap-around EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_3); EXPECT_EQ(latest_receive_time_ms, receive_time_3); } TEST_F(RtpReceiverTest, UpdatesTimestampsWhenStreamResets) { RTPHeader header; int64_t time1_ms = fake_clock_.TimeInMilliseconds(); header.payloadType = kPcmuPayloadType; header.ssrc = kSsrc1; header.timestamp = rtp_timestamp(time1_ms); header.extension.hasAudioLevel = true; header.extension.audioLevel = 10; header.sequenceNumber = 0xfff0; const PayloadUnion payload_specific{ AudioPayload{SdpAudioFormat("foo", 8000, 1), 0}}; uint32_t latest_timestamp; int64_t latest_receive_time_ms; // No packet received yet. EXPECT_FALSE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); // Initial packet const uint32_t timestamp_1 = header.timestamp; const int64_t receive_time_1 = fake_clock_.TimeInMilliseconds(); const uint16_t seqno_1 = header.sequenceNumber; EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_1); EXPECT_EQ(latest_receive_time_ms, receive_time_1); // Packet with far in the past seqno, but unlikely to be a wrap-around. // Treated as a seqno discontinuity, and timestamp is recorded. fake_clock_.AdvanceTimeMilliseconds(10); header.timestamp += 900; header.sequenceNumber = 0x9000; const uint32_t timestamp_2 = header.timestamp; const int64_t receive_time_2 = fake_clock_.TimeInMilliseconds(); const uint16_t seqno_2 = header.sequenceNumber; EXPECT_LT(seqno_1 - seqno_2, 0x8000); // In the past. EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket( header, kTestPayload, sizeof(kTestPayload), payload_specific)); EXPECT_TRUE(rtp_receiver_->GetLatestTimestamps(&latest_timestamp, &latest_receive_time_ms)); EXPECT_EQ(latest_timestamp, timestamp_2); EXPECT_EQ(latest_receive_time_ms, receive_time_2); } } // namespace webrtc