blob: fd7b8a7f168140ea7213d2a1d2675c72c0f84881 [file] [log] [blame]
/*
* 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;
const uint32_t kSsrc = 3456;
const uint32_t kTimestamp = 4567;
struct CngCodecSpec {
int payload_type;
int clockrate_hz;
};
const CngCodecSpec kCngCodecs[] = {{13, 8000},
{103, 16000},
{104, 32000},
{105, 48000}};
// Rough sanity check of DTMF payload.
void VerifyDtmf(const uint8_t* payloadData,
size_t payloadSize) {
EXPECT_EQ(payloadSize, 4u);
uint8_t p0 = (payloadSize > 0) ? payloadData[0] : 0xff;
uint8_t p1 = (payloadSize > 1) ? payloadData[1] : 0xff;
uint8_t p2 = (payloadSize > 2) ? payloadData[2] : 0xff;
uint8_t p3 = (payloadSize > 3) ? payloadData[3] : 0xff;
uint8_t event = p0;
bool reserved = (p1 >> 6) & 1;
uint8_t volume = p1 & 63;
uint16_t duration = (p2 << 8) | p3;
// 0-15 are digits, #, *, A-D, 32 is answer tone (see rfc 4734)
EXPECT_LE(event, 32u);
EXPECT_TRUE(event < 16u || event == 32u);
EXPECT_FALSE(reserved);
EXPECT_EQ(volume, 10u);
// Long duration for answer tone events only
EXPECT_TRUE(duration <= 1280 || event == 32u);
}
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) {
EXPECT_EQ(sizeof(kTestPayload), payloadSize);
// All our test vectors for PCMU are equal to |kTestPayload|.
const size_t min_size = std::min(sizeof(kTestPayload), payloadSize);
EXPECT_EQ(0, memcmp(payloadData, kTestPayload, min_size));
} else if (payload_type == kDtmfPayloadType) {
VerifyDtmf(payloadData, payloadSize);
}
return 0;
}
};
} // namespace
class RtpRtcpAudioTest : public ::testing::Test {
protected:
RtpRtcpAudioTest()
: fake_clock_(123456),
retransmission_rate_limiter_(&fake_clock_, 1000),
receive_statistics1_(ReceiveStatistics::Create(&fake_clock_)),
receive_statistics2_(ReceiveStatistics::Create(&fake_clock_)),
rtp_receiver1_(
RtpReceiver::CreateAudioReceiver(&fake_clock_,
&data_receiver1_,
&rtp_payload_registry1_)),
rtp_receiver2_(
RtpReceiver::CreateAudioReceiver(&fake_clock_,
&data_receiver2_,
&rtp_payload_registry2_)) {
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));
configuration.receive_statistics = receive_statistics2_.get();
configuration.outgoing_transport = &transport2_;
module2_.reset(RtpRtcp::CreateRtpRtcp(configuration));
transport1_.SetSendModule(module2_.get(), &rtp_payload_registry2_,
rtp_receiver2_.get(), receive_statistics2_.get());
transport2_.SetSendModule(module1_.get(), &rtp_payload_registry1_,
rtp_receiver1_.get(), receive_statistics1_.get());
}
~RtpRtcpAudioTest() override = default;
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)));
}
SimulatedClock fake_clock_;
RateLimiter retransmission_rate_limiter_;
VerifyingAudioReceiver data_receiver1_;
VerifyingAudioReceiver data_receiver2_;
std::unique_ptr<ReceiveStatistics> receive_statistics1_;
std::unique_ptr<ReceiveStatistics> receive_statistics2_;
RTPPayloadRegistry rtp_payload_registry1_;
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_;
};
TEST_F(RtpRtcpAudioTest, Basic) {
module1_->SetSSRC(kSsrc);
module1_->SetStartTimestamp(kTimestamp);
// 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(kSsrc, rtp_receiver2_->SSRC());
uint32_t timestamp;
int64_t receive_time_ms;
EXPECT_TRUE(
rtp_receiver2_->GetLatestTimestamps(&timestamp, &receive_time_ms));
EXPECT_EQ(kTimestamp, 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(kSsrc);
module1_->SetStartTimestamp(kTimestamp);
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(kSsrc);
module1_->SetStartTimestamp(kTimestamp);
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(kSsrc, rtp_receiver2_->SSRC());
EXPECT_TRUE(
rtp_receiver2_->GetLatestTimestamps(&timestamp, &receive_time_ms));
EXPECT_EQ(kTimestamp + 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(kSsrc, rtp_receiver2_->SSRC());
EXPECT_TRUE(
rtp_receiver2_->GetLatestTimestamps(&timestamp, &receive_time_ms));
EXPECT_EQ(kTimestamp + in_timestamp, timestamp);
EXPECT_EQ(fake_clock_.TimeInMilliseconds(), receive_time_ms);
in_timestamp += 10;
fake_clock_.AdvanceTimeMilliseconds(20);
}
}
} // namespace webrtc