| /* | |
| * Copyright (c) 2012 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 <algorithm> | |
| #include <memory> | |
| #include <vector> | |
| #include "common_types.h" // NOLINT(build/include) | |
| #include "modules/audio_coding/codecs/audio_format_conversion.h" | |
| #include "modules/rtp_rtcp/include/rtp_rtcp.h" | |
| #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | |
| #include "modules/rtp_rtcp/source/rtp_receiver_audio.h" | |
| #include "modules/rtp_rtcp/test/testAPI/test_api.h" | |
| #include "rtc_base/rate_limiter.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| namespace { | |
| const uint32_t kTestRate = 64000u; | |
| const uint8_t kTestPayload[] = { 't', 'e', 's', 't' }; | |
| const uint8_t kPcmuPayloadType = 96; | |
| const uint8_t kDtmfPayloadType = 97; | |
| struct CngCodecSpec { | |
| int payload_type; | |
| int clockrate_hz; | |
| }; | |
| const CngCodecSpec kCngCodecs[] = {{13, 8000}, | |
| {103, 16000}, | |
| {104, 32000}, | |
| {105, 48000}}; | |
| bool IsComfortNoisePayload(uint8_t payload_type) { | |
| for (const auto& c : kCngCodecs) { | |
| if (c.payload_type == payload_type) | |
| return true; | |
| } | |
| return false; | |
| } | |
| class VerifyingAudioReceiver : public RtpData { | |
| public: | |
| int32_t OnReceivedPayloadData( | |
| const uint8_t* payloadData, | |
| size_t payloadSize, | |
| const webrtc::WebRtcRTPHeader* rtpHeader) override { | |
| const uint8_t payload_type = rtpHeader->header.payloadType; | |
| if (payload_type == kPcmuPayloadType || payload_type == kDtmfPayloadType) { | |
| EXPECT_EQ(sizeof(kTestPayload), payloadSize); | |
| // All our test vectors for PCMU and DTMF are equal to |kTestPayload|. | |
| const size_t min_size = std::min(sizeof(kTestPayload), payloadSize); | |
| EXPECT_EQ(0, memcmp(payloadData, kTestPayload, min_size)); | |
| } else if (IsComfortNoisePayload(payload_type)) { | |
| // CNG types should be recognized properly. | |
| EXPECT_EQ(kAudioFrameCN, rtpHeader->frameType); | |
| EXPECT_TRUE(rtpHeader->type.Audio.isCNG); | |
| } | |
| return 0; | |
| } | |
| }; | |
| } // namespace | |
| class RtpRtcpAudioTest : public ::testing::Test { | |
| protected: | |
| RtpRtcpAudioTest() | |
| : fake_clock(123456), retransmission_rate_limiter_(&fake_clock, 1000) { | |
| test_CSRC[0] = 1234; | |
| test_CSRC[2] = 2345; | |
| test_ssrc = 3456; | |
| test_timestamp = 4567; | |
| test_sequence_number = 2345; | |
| } | |
| ~RtpRtcpAudioTest() override = default; | |
| void SetUp() override { | |
| receive_statistics1_.reset(ReceiveStatistics::Create(&fake_clock)); | |
| receive_statistics2_.reset(ReceiveStatistics::Create(&fake_clock)); | |
| rtp_payload_registry1_.reset(new RTPPayloadRegistry()); | |
| rtp_payload_registry2_.reset(new RTPPayloadRegistry()); | |
| RtpRtcp::Configuration configuration; | |
| configuration.audio = true; | |
| configuration.clock = &fake_clock; | |
| configuration.receive_statistics = receive_statistics1_.get(); | |
| configuration.outgoing_transport = &transport1; | |
| configuration.retransmission_rate_limiter = &retransmission_rate_limiter_; | |
| module1.reset(RtpRtcp::CreateRtpRtcp(configuration)); | |
| rtp_receiver1_.reset(RtpReceiver::CreateAudioReceiver( | |
| &fake_clock, &data_receiver1, rtp_payload_registry1_.get())); | |
| configuration.receive_statistics = receive_statistics2_.get(); | |
| configuration.outgoing_transport = &transport2; | |
| module2.reset(RtpRtcp::CreateRtpRtcp(configuration)); | |
| rtp_receiver2_.reset(RtpReceiver::CreateAudioReceiver( | |
| &fake_clock, &data_receiver2, rtp_payload_registry2_.get())); | |
| transport1.SetSendModule(module2.get(), rtp_payload_registry2_.get(), | |
| rtp_receiver2_.get(), receive_statistics2_.get()); | |
| transport2.SetSendModule(module1.get(), rtp_payload_registry1_.get(), | |
| rtp_receiver1_.get(), receive_statistics1_.get()); | |
| } | |
| void RegisterPayload(const CodecInst& codec) { | |
| EXPECT_EQ(0, module1->RegisterSendPayload(codec)); | |
| EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload(codec.pltype, | |
| CodecInstToSdp(codec))); | |
| EXPECT_EQ(0, module2->RegisterSendPayload(codec)); | |
| EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(codec.pltype, | |
| CodecInstToSdp(codec))); | |
| } | |
| VerifyingAudioReceiver data_receiver1; | |
| VerifyingAudioReceiver data_receiver2; | |
| std::unique_ptr<ReceiveStatistics> receive_statistics1_; | |
| std::unique_ptr<ReceiveStatistics> receive_statistics2_; | |
| std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry1_; | |
| std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry2_; | |
| std::unique_ptr<RtpReceiver> rtp_receiver1_; | |
| std::unique_ptr<RtpReceiver> rtp_receiver2_; | |
| std::unique_ptr<RtpRtcp> module1; | |
| std::unique_ptr<RtpRtcp> module2; | |
| LoopBackTransport transport1; | |
| LoopBackTransport transport2; | |
| uint32_t test_ssrc; | |
| uint32_t test_timestamp; | |
| uint16_t test_sequence_number; | |
| uint32_t test_CSRC[webrtc::kRtpCsrcSize]; | |
| SimulatedClock fake_clock; | |
| RateLimiter retransmission_rate_limiter_; | |
| }; | |
| TEST_F(RtpRtcpAudioTest, Basic) { | |
| module1->SetSSRC(test_ssrc); | |
| module1->SetStartTimestamp(test_timestamp); | |
| // Test detection at the end of a DTMF tone. | |
| // EXPECT_EQ(0, module2->SetTelephoneEventForwardToDecoder(true)); | |
| EXPECT_EQ(0, module1->SetSendingStatus(true)); | |
| // Start basic RTP test. | |
| // Send an empty RTP packet. | |
| // Should fail since we have not registered the payload type. | |
| EXPECT_FALSE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech, | |
| kPcmuPayloadType, 0, -1, nullptr, 0, | |
| nullptr, nullptr, nullptr)); | |
| CodecInst voice_codec = {}; | |
| voice_codec.pltype = kPcmuPayloadType; | |
| voice_codec.plfreq = 8000; | |
| voice_codec.rate = kTestRate; | |
| memcpy(voice_codec.plname, "PCMU", 5); | |
| RegisterPayload(voice_codec); | |
| EXPECT_TRUE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech, | |
| kPcmuPayloadType, 0, -1, kTestPayload, | |
| 4, nullptr, nullptr, nullptr)); | |
| EXPECT_EQ(test_ssrc, rtp_receiver2_->SSRC()); | |
| uint32_t timestamp; | |
| int64_t receive_time_ms; | |
| EXPECT_TRUE( | |
| rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms)); | |
| EXPECT_EQ(test_timestamp, timestamp); | |
| EXPECT_EQ(fake_clock.TimeInMilliseconds(), receive_time_ms); | |
| } | |
| TEST_F(RtpRtcpAudioTest, DTMF) { | |
| CodecInst voice_codec = {}; | |
| voice_codec.pltype = kPcmuPayloadType; | |
| voice_codec.plfreq = 8000; | |
| voice_codec.rate = kTestRate; | |
| memcpy(voice_codec.plname, "PCMU", 5); | |
| RegisterPayload(voice_codec); | |
| module1->SetSSRC(test_ssrc); | |
| module1->SetStartTimestamp(test_timestamp); | |
| EXPECT_EQ(0, module1->SetSendingStatus(true)); | |
| // Prepare for DTMF. | |
| voice_codec.pltype = kDtmfPayloadType; | |
| voice_codec.plfreq = 8000; | |
| memcpy(voice_codec.plname, "telephone-event", 16); | |
| EXPECT_EQ(0, module1->RegisterSendPayload(voice_codec)); | |
| EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload( | |
| voice_codec.pltype, CodecInstToSdp(voice_codec))); | |
| // Start DTMF test. | |
| int timeStamp = 160; | |
| // Send a DTMF tone using RFC 2833 (4733). | |
| for (int i = 0; i < 16; i++) { | |
| EXPECT_EQ(0, module1->SendTelephoneEventOutband(i, timeStamp, 10)); | |
| } | |
| timeStamp += 160; // Prepare for next packet. | |
| // Send RTP packets for 16 tones a 160 ms 100ms | |
| // pause between = 2560ms + 1600ms = 4160ms | |
| for (; timeStamp <= 250 * 160; timeStamp += 160) { | |
| EXPECT_TRUE(module1->SendOutgoingData( | |
| webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1, | |
| kTestPayload, 4, nullptr, nullptr, nullptr)); | |
| fake_clock.AdvanceTimeMilliseconds(20); | |
| module1->Process(); | |
| } | |
| EXPECT_EQ(0, module1->SendTelephoneEventOutband(32, 9000, 10)); | |
| for (; timeStamp <= 740 * 160; timeStamp += 160) { | |
| EXPECT_TRUE(module1->SendOutgoingData( | |
| webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1, | |
| kTestPayload, 4, nullptr, nullptr, nullptr)); | |
| fake_clock.AdvanceTimeMilliseconds(20); | |
| module1->Process(); | |
| } | |
| } | |
| TEST_F(RtpRtcpAudioTest, ComfortNoise) { | |
| module1->SetSSRC(test_ssrc); | |
| module1->SetStartTimestamp(test_timestamp); | |
| EXPECT_EQ(0, module1->SetSendingStatus(true)); | |
| // Register PCMU and all four comfort noise codecs | |
| CodecInst voice_codec = {}; | |
| voice_codec.pltype = kPcmuPayloadType; | |
| voice_codec.plfreq = 8000; | |
| voice_codec.rate = kTestRate; | |
| memcpy(voice_codec.plname, "PCMU", 5); | |
| RegisterPayload(voice_codec); | |
| for (const auto& c : kCngCodecs) { | |
| CodecInst cng_codec = {}; | |
| cng_codec.pltype = c.payload_type; | |
| cng_codec.plfreq = c.clockrate_hz; | |
| memcpy(cng_codec.plname, "CN", 3); | |
| RegisterPayload(cng_codec); | |
| } | |
| // Transmit comfort noise packets interleaved by PCMU packets. | |
| uint32_t in_timestamp = 0; | |
| for (const auto& c : kCngCodecs) { | |
| uint32_t timestamp; | |
| int64_t receive_time_ms; | |
| EXPECT_TRUE(module1->SendOutgoingData( | |
| webrtc::kAudioFrameSpeech, kPcmuPayloadType, in_timestamp, -1, | |
| kTestPayload, 4, nullptr, nullptr, nullptr)); | |
| EXPECT_EQ(test_ssrc, rtp_receiver2_->SSRC()); | |
| EXPECT_TRUE( | |
| rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms)); | |
| EXPECT_EQ(test_timestamp + in_timestamp, timestamp); | |
| EXPECT_EQ(fake_clock.TimeInMilliseconds(), receive_time_ms); | |
| in_timestamp += 10; | |
| fake_clock.AdvanceTimeMilliseconds(20); | |
| EXPECT_TRUE(module1->SendOutgoingData(webrtc::kAudioFrameCN, c.payload_type, | |
| in_timestamp, -1, kTestPayload, 1, | |
| nullptr, nullptr, nullptr)); | |
| EXPECT_EQ(test_ssrc, rtp_receiver2_->SSRC()); | |
| EXPECT_TRUE( | |
| rtp_receiver2_->GetLatestTimestamps(×tamp, &receive_time_ms)); | |
| EXPECT_EQ(test_timestamp + in_timestamp, timestamp); | |
| EXPECT_EQ(fake_clock.TimeInMilliseconds(), receive_time_ms); | |
| in_timestamp += 10; | |
| fake_clock.AdvanceTimeMilliseconds(20); | |
| } | |
| } | |
| } // namespace webrtc |