Resurrected test_api_audio.cc

I'll be doing some changes to code it tests (rtp_receiver_audio,
specifically) and want to make sure there are tests in place before I
touch anything.

Fixed test_api_audio not properly checking payload data. Required a
fix to LoopBackTransport in test_api to as to act like the regular
audio and video parts of WebRTC and separate payload from header data.

Also added a test for CNG and cleaned up constants.

BUG=webrtc:5805

Review-Url: https://codereview.webrtc.org/2378403004
Cr-Commit-Position: refs/heads/master@{#14529}
This commit is contained in:
ossu
2016-10-05 07:51:44 -07:00
committed by Commit bot
parent b1fff92644
commit b2d1e0d1da
2 changed files with 148 additions and 120 deletions

View File

@ -14,6 +14,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/base/rate_limiter.h" #include "webrtc/base/rate_limiter.h"
#include "webrtc/test/null_transport.h" #include "webrtc/test/null_transport.h"
@ -44,7 +45,7 @@ bool LoopBackTransport::SendRtp(const uint8_t* data,
} }
RTPHeader header; RTPHeader header;
std::unique_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create()); std::unique_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
if (!parser->Parse(static_cast<const uint8_t*>(data), len, &header)) { if (!parser->Parse(data, len, &header)) {
return false; return false;
} }
PayloadUnion payload_specific; PayloadUnion payload_specific;
@ -52,9 +53,11 @@ bool LoopBackTransport::SendRtp(const uint8_t* data,
&payload_specific)) { &payload_specific)) {
return false; return false;
} }
const uint8_t* payload = data + header.headerLength;
RTC_CHECK_GE(len, header.headerLength);
const size_t payload_length = len - header.headerLength;
receive_statistics_->IncomingPacket(header, len, false); receive_statistics_->IncomingPacket(header, len, false);
if (!rtp_receiver_->IncomingRtpPacket(header, if (!rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
static_cast<const uint8_t*>(data), len,
payload_specific, true)) { payload_specific, true)) {
return false; return false;
} }

View File

@ -22,7 +22,30 @@
namespace webrtc { namespace webrtc {
namespace { namespace {
#define test_rate 64000u
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 NullRtpData { class VerifyingAudioReceiver : public NullRtpData {
public: public:
@ -30,31 +53,16 @@ class VerifyingAudioReceiver : public NullRtpData {
const uint8_t* payloadData, const uint8_t* payloadData,
size_t payloadSize, size_t payloadSize,
const webrtc::WebRtcRTPHeader* rtpHeader) override { const webrtc::WebRtcRTPHeader* rtpHeader) override {
if (rtpHeader->header.payloadType == 98 || const uint8_t payload_type = rtpHeader->header.payloadType;
rtpHeader->header.payloadType == 99) { if (payload_type == kPcmuPayloadType || payload_type == kDtmfPayloadType) {
EXPECT_EQ(4u, payloadSize); EXPECT_EQ(sizeof(kTestPayload), payloadSize);
char str[5]; // All our test vectors for PCMU and DTMF are equal to |kTestPayload|.
memcpy(str, payloadData, payloadSize); const size_t min_size = std::min(sizeof(kTestPayload), payloadSize);
str[4] = 0; EXPECT_EQ(0, memcmp(payloadData, kTestPayload, min_size));
// All our test vectors for payload type 96 and 97 even the stereo is on } else if (IsComfortNoisePayload(payload_type)) {
// a per channel base equal to the 4 chars "test". // CNG types should be recognized properly.
// Note there is no null termination so we add that to use the EXPECT_EQ(kAudioFrameCN, rtpHeader->frameType);
// test EXPECT_STRCASEEQ. EXPECT_TRUE(rtpHeader->type.Audio.isCNG);
EXPECT_STRCASEEQ("test", str);
return 0;
}
if (rtpHeader->header.payloadType == 100 ||
rtpHeader->header.payloadType == 101 ||
rtpHeader->header.payloadType == 102) {
if (rtpHeader->type.Audio.channel == 1) {
if (payloadData[0] == 0xff) {
// All our test vectors for payload type 100, 101 and 102 have the
// first channel data being equal to 0xff.
return 0;
}
}
ADD_FAILURE() << "This code path should never happen.";
return -1;
} }
return 0; return 0;
} }
@ -67,14 +75,16 @@ class RTPCallback : public NullRtpFeedback {
const int frequency, const int frequency,
const size_t channels, const size_t channels,
const uint32_t rate) override { const uint32_t rate) override {
if (payloadType == 96) { if (payloadType == kPcmuPayloadType) {
EXPECT_EQ(test_rate, rate) << EXPECT_EQ(kTestRate, rate) <<
"The rate should be 64K for this payloadType"; "The rate should be 64K for this payloadType";
} }
return 0; return 0;
} }
}; };
} // namespace
class RtpRtcpAudioTest : public ::testing::Test { class RtpRtcpAudioTest : public ::testing::Test {
protected: protected:
RtpRtcpAudioTest() RtpRtcpAudioTest()
@ -88,12 +98,6 @@ class RtpRtcpAudioTest : public ::testing::Test {
~RtpRtcpAudioTest() {} ~RtpRtcpAudioTest() {}
void SetUp() override { void SetUp() override {
data_receiver1 = new VerifyingAudioReceiver();
data_receiver2 = new VerifyingAudioReceiver();
rtp_callback = new RTPCallback();
transport1 = new LoopBackTransport();
transport2 = new LoopBackTransport();
receive_statistics1_.reset(ReceiveStatistics::Create(&fake_clock)); receive_statistics1_.reset(ReceiveStatistics::Create(&fake_clock));
receive_statistics2_.reset(ReceiveStatistics::Create(&fake_clock)); receive_statistics2_.reset(ReceiveStatistics::Create(&fake_clock));
@ -106,49 +110,58 @@ class RtpRtcpAudioTest : public ::testing::Test {
configuration.audio = true; configuration.audio = true;
configuration.clock = &fake_clock; configuration.clock = &fake_clock;
configuration.receive_statistics = receive_statistics1_.get(); configuration.receive_statistics = receive_statistics1_.get();
configuration.outgoing_transport = transport1; configuration.outgoing_transport = &transport1;
configuration.retransmission_rate_limiter = &retransmission_rate_limiter_; configuration.retransmission_rate_limiter = &retransmission_rate_limiter_;
module1 = RtpRtcp::CreateRtpRtcp(configuration); module1.reset(RtpRtcp::CreateRtpRtcp(configuration));
rtp_receiver1_.reset(RtpReceiver::CreateAudioReceiver( rtp_receiver1_.reset(RtpReceiver::CreateAudioReceiver(
&fake_clock, data_receiver1, NULL, rtp_payload_registry1_.get())); &fake_clock, &data_receiver1, &rtp_callback,
rtp_payload_registry1_.get()));
configuration.receive_statistics = receive_statistics2_.get(); configuration.receive_statistics = receive_statistics2_.get();
configuration.outgoing_transport = transport2; configuration.outgoing_transport = &transport2;
module2 = RtpRtcp::CreateRtpRtcp(configuration); module2.reset(RtpRtcp::CreateRtpRtcp(configuration));
rtp_receiver2_.reset(RtpReceiver::CreateAudioReceiver( rtp_receiver2_.reset(RtpReceiver::CreateAudioReceiver(
&fake_clock, data_receiver2, NULL, rtp_payload_registry2_.get())); &fake_clock, &data_receiver2, &rtp_callback,
rtp_payload_registry2_.get()));
transport1->SetSendModule(module2, rtp_payload_registry2_.get(), transport1.SetSendModule(module2.get(), rtp_payload_registry2_.get(),
rtp_receiver2_.get(), receive_statistics2_.get()); rtp_receiver2_.get(), receive_statistics2_.get());
transport2->SetSendModule(module1, rtp_payload_registry1_.get(), transport2.SetSendModule(module1.get(), rtp_payload_registry1_.get(),
rtp_receiver1_.get(), receive_statistics1_.get()); rtp_receiver1_.get(), receive_statistics1_.get());
} }
void TearDown() override { void RegisterPayload(const CodecInst& codec) {
delete module1; EXPECT_EQ(0, module1->RegisterSendPayload(codec));
delete module2; EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload(
delete transport1; codec.plname,
delete transport2; codec.pltype,
delete data_receiver1; codec.plfreq,
delete data_receiver2; codec.channels,
delete rtp_callback; codec.rate));
EXPECT_EQ(0, module2->RegisterSendPayload(codec));
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(
codec.plname,
codec.pltype,
codec.plfreq,
codec.channels,
codec.rate));
} }
RtpRtcp* module1; VerifyingAudioReceiver data_receiver1;
RtpRtcp* module2; VerifyingAudioReceiver data_receiver2;
RTPCallback rtp_callback;
std::unique_ptr<ReceiveStatistics> receive_statistics1_; std::unique_ptr<ReceiveStatistics> receive_statistics1_;
std::unique_ptr<ReceiveStatistics> receive_statistics2_; std::unique_ptr<ReceiveStatistics> receive_statistics2_;
std::unique_ptr<RtpReceiver> rtp_receiver1_;
std::unique_ptr<RtpReceiver> rtp_receiver2_;
std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry1_; std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry1_;
std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry2_; std::unique_ptr<RTPPayloadRegistry> rtp_payload_registry2_;
VerifyingAudioReceiver* data_receiver1; std::unique_ptr<RtpReceiver> rtp_receiver1_;
VerifyingAudioReceiver* data_receiver2; std::unique_ptr<RtpReceiver> rtp_receiver2_;
LoopBackTransport* transport1; std::unique_ptr<RtpRtcp> module1;
LoopBackTransport* transport2; std::unique_ptr<RtpRtcp> module2;
RTPCallback* rtp_callback; LoopBackTransport transport1;
LoopBackTransport transport2;
uint32_t test_ssrc; uint32_t test_ssrc;
uint32_t test_timestamp; uint32_t test_timestamp;
uint16_t test_sequence_number; uint16_t test_sequence_number;
@ -170,36 +183,20 @@ TEST_F(RtpRtcpAudioTest, Basic) {
// Send an empty RTP packet. // Send an empty RTP packet.
// Should fail since we have not registered the payload type. // Should fail since we have not registered the payload type.
EXPECT_FALSE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, 0, -1, EXPECT_FALSE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech,
nullptr, 0, nullptr, nullptr, kPcmuPayloadType, 0, -1, nullptr, 0,
nullptr)); nullptr, nullptr, nullptr));
CodecInst voice_codec; CodecInst voice_codec = {};
memset(&voice_codec, 0, sizeof(voice_codec)); voice_codec.pltype = kPcmuPayloadType;
voice_codec.pltype = 96;
voice_codec.plfreq = 8000; voice_codec.plfreq = 8000;
voice_codec.rate = kTestRate;
memcpy(voice_codec.plname, "PCMU", 5); memcpy(voice_codec.plname, "PCMU", 5);
RegisterPayload(voice_codec);
EXPECT_EQ(0, module1->RegisterSendPayload(voice_codec)); EXPECT_TRUE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech,
EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload( kPcmuPayloadType, 0, -1, kTestPayload,
voice_codec.plname, 4, nullptr, nullptr, nullptr));
voice_codec.pltype,
voice_codec.plfreq,
voice_codec.channels,
(voice_codec.rate < 0) ? 0 : voice_codec.rate));
EXPECT_EQ(0, module2->RegisterSendPayload(voice_codec));
voice_codec.rate = test_rate;
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(
voice_codec.plname,
voice_codec.pltype,
voice_codec.plfreq,
voice_codec.channels,
(voice_codec.rate < 0) ? 0 : voice_codec.rate));
const uint8_t test[5] = "test";
EXPECT_EQ(true,
module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, 0, -1,
test, 4, nullptr, nullptr, nullptr));
EXPECT_EQ(test_ssrc, rtp_receiver2_->SSRC()); EXPECT_EQ(test_ssrc, rtp_receiver2_->SSRC());
uint32_t timestamp; uint32_t timestamp;
@ -208,34 +205,19 @@ TEST_F(RtpRtcpAudioTest, Basic) {
} }
TEST_F(RtpRtcpAudioTest, DTMF) { TEST_F(RtpRtcpAudioTest, DTMF) {
CodecInst voice_codec; CodecInst voice_codec = {};
memset(&voice_codec, 0, sizeof(voice_codec)); voice_codec.pltype = kPcmuPayloadType;
voice_codec.pltype = 96;
voice_codec.plfreq = 8000; voice_codec.plfreq = 8000;
voice_codec.rate = kTestRate;
memcpy(voice_codec.plname, "PCMU", 5); memcpy(voice_codec.plname, "PCMU", 5);
RegisterPayload(voice_codec);
EXPECT_EQ(0, module1->RegisterSendPayload(voice_codec));
EXPECT_EQ(0, rtp_receiver1_->RegisterReceivePayload(
voice_codec.plname,
voice_codec.pltype,
voice_codec.plfreq,
voice_codec.channels,
(voice_codec.rate < 0) ? 0 : voice_codec.rate));
EXPECT_EQ(0, module2->RegisterSendPayload(voice_codec));
voice_codec.rate = test_rate;
EXPECT_EQ(0, rtp_receiver2_->RegisterReceivePayload(
voice_codec.plname,
voice_codec.pltype,
voice_codec.plfreq,
voice_codec.channels,
(voice_codec.rate < 0) ? 0 : voice_codec.rate));
module1->SetSSRC(test_ssrc); module1->SetSSRC(test_ssrc);
module1->SetStartTimestamp(test_timestamp); module1->SetStartTimestamp(test_timestamp);
EXPECT_EQ(0, module1->SetSendingStatus(true)); EXPECT_EQ(0, module1->SetSendingStatus(true));
// Prepare for DTMF. // Prepare for DTMF.
voice_codec.pltype = 97; voice_codec.pltype = kDtmfPayloadType;
voice_codec.plfreq = 8000; voice_codec.plfreq = 8000;
memcpy(voice_codec.plname, "telephone-event", 16); memcpy(voice_codec.plname, "telephone-event", 16);
@ -245,7 +227,7 @@ TEST_F(RtpRtcpAudioTest, DTMF) {
voice_codec.pltype, voice_codec.pltype,
voice_codec.plfreq, voice_codec.plfreq,
voice_codec.channels, voice_codec.channels,
(voice_codec.rate < 0) ? 0 : voice_codec.rate)); voice_codec.rate));
// Start DTMF test. // Start DTMF test.
int timeStamp = 160; int timeStamp = 160;
@ -256,27 +238,70 @@ TEST_F(RtpRtcpAudioTest, DTMF) {
} }
timeStamp += 160; // Prepare for next packet. timeStamp += 160; // Prepare for next packet.
const uint8_t test[9] = "test";
// Send RTP packets for 16 tones a 160 ms 100ms // Send RTP packets for 16 tones a 160 ms 100ms
// pause between = 2560ms + 1600ms = 4160ms // pause between = 2560ms + 1600ms = 4160ms
for (; timeStamp <= 250 * 160; timeStamp += 160) { for (; timeStamp <= 250 * 160; timeStamp += 160) {
EXPECT_TRUE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, EXPECT_TRUE(module1->SendOutgoingData(
timeStamp, -1, test, 4, nullptr, webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1,
nullptr, nullptr)); kTestPayload, 4, nullptr, nullptr, nullptr));
fake_clock.AdvanceTimeMilliseconds(20); fake_clock.AdvanceTimeMilliseconds(20);
module1->Process(); module1->Process();
} }
EXPECT_EQ(0, module1->SendTelephoneEventOutband(32, 9000, 10)); EXPECT_EQ(0, module1->SendTelephoneEventOutband(32, 9000, 10));
for (; timeStamp <= 740 * 160; timeStamp += 160) { for (; timeStamp <= 740 * 160; timeStamp += 160) {
EXPECT_TRUE(module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96, EXPECT_TRUE(module1->SendOutgoingData(
timeStamp, -1, test, 4, nullptr, webrtc::kAudioFrameSpeech, kPcmuPayloadType, timeStamp, -1,
nullptr, nullptr)); kTestPayload, 4, nullptr, nullptr, nullptr));
fake_clock.AdvanceTimeMilliseconds(20); fake_clock.AdvanceTimeMilliseconds(20);
module1->Process(); module1->Process();
} }
} }
} // namespace 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;
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_->Timestamp(&timestamp));
EXPECT_EQ(test_timestamp + in_timestamp, timestamp);
in_timestamp += 10;
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_->Timestamp(&timestamp));
EXPECT_EQ(test_timestamp + in_timestamp, timestamp);
in_timestamp += 10;
}
}
} // namespace webrtc } // namespace webrtc