Adding regression test to NetEQ

The test inputs RTP packets from an RTPdump file into NetEQ
and compares the output to the corresponding reference file.
Test files are included.

The change also includes a new method in NETEQTEST_RTPpacket
class, which reads past the initial file header in an RTPdump
file.

Finally, a few warnings are removed.
Review URL: http://webrtc-codereview.appspot.com/138012

git-svn-id: http://webrtc.googlecode.com/svn/trunk@568 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org 2011-09-09 08:01:16 +00:00
parent c273019768
commit 35dcc23110
8 changed files with 258 additions and 64 deletions

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2011 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.
*/
/*
* This file includes unit tests for NetEQ.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // memset
#include <vector>
#include "gtest/gtest.h"
#include "modules/audio_coding/NetEQ/main/test/NETEQTEST_CodecClass.h"
#include "modules/audio_coding/NetEQ/main/test/NETEQTEST_NetEQClass.h"
#include "modules/audio_coding/NetEQ/main/test/NETEQTEST_RTPpacket.h"
#include "typedefs.h" // NOLINT(build/include)
#include "modules/audio_coding/NetEQ/main/interface/webrtc_neteq.h"
#include "modules/audio_coding/NetEQ/main/interface/webrtc_neteq_help_macros.h"
namespace {
class NetEqDecodingTest : public ::testing::Test {
protected:
NetEqDecodingTest();
virtual void SetUp();
virtual void TearDown();
void SelectDecoders(WebRtcNetEQDecoder* used_codec);
void LoadDecoders();
void DecodeAndCompare(const char* rtp_file, const char* ref_file);
NETEQTEST_NetEQClass* neteq_inst_;
std::vector<NETEQTEST_Decoder*> dec_;
};
NetEqDecodingTest::NetEqDecodingTest() : neteq_inst_(NULL) {}
void NetEqDecodingTest::SetUp() {
WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd - 1];
SelectDecoders(usedCodec);
neteq_inst_ = new NETEQTEST_NetEQClass(usedCodec, dec_.size(), 8000,
kTCPLargeJitter);
ASSERT_TRUE(neteq_inst_);
LoadDecoders();
}
void NetEqDecodingTest::TearDown() {
if (neteq_inst_)
delete neteq_inst_;
for (size_t i = 0; i < dec_.size(); ++i) {
if (dec_[i])
delete dec_[i];
}
}
void NetEqDecodingTest::SelectDecoders(WebRtcNetEQDecoder* used_codec) {
*used_codec++ = kDecoderPCMu;
dec_.push_back(new decoder_PCMU(0));
*used_codec++ = kDecoderPCMa;
dec_.push_back(new decoder_PCMA(8));
*used_codec++ = kDecoderILBC;
dec_.push_back(new decoder_ILBC(102));
*used_codec++ = kDecoderISAC;
dec_.push_back(new decoder_iSAC(103));
*used_codec++ = kDecoderISACswb;
dec_.push_back(new decoder_iSACSWB(104));
*used_codec++ = kDecoderPCM16B;
dec_.push_back(new decoder_PCM16B_NB(93));
*used_codec++ = kDecoderPCM16Bwb;
dec_.push_back(new decoder_PCM16B_WB(94));
*used_codec++ = kDecoderPCM16Bswb32kHz;
dec_.push_back(new decoder_PCM16B_SWB32(95));
*used_codec++ = kDecoderCNG;
dec_.push_back(new decoder_CNG(13));
}
void NetEqDecodingTest::LoadDecoders() {
for (size_t i = 0; i < dec_.size(); ++i) {
ASSERT_EQ(0, dec_[i]->loadToNetEQ(*neteq_inst_));
}
}
void NetEqDecodingTest::DecodeAndCompare(const char* rtp_file,
const char* ref_file) {
NETEQTEST_RTPpacket rtp;
FILE* rtp_fp = fopen(rtp_file, "rb");
ASSERT_TRUE(rtp_fp != NULL);
ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp));
ASSERT_GT(rtp.readFromFile(rtp_fp), 0);
FILE* ref_fp = fopen(ref_file, "rb");
ASSERT_TRUE(ref_fp != NULL);
unsigned int sim_clock = 0;
const int kTimeStep = 10;
while (rtp.dataLen() >= 0) {
// Check if time to receive.
while ((sim_clock >= rtp.time()) &&
(rtp.dataLen() >= 0)) {
if (rtp.dataLen() > 0) {
ASSERT_EQ(0, neteq_inst_->recIn(rtp));
}
// Get next packet.
ASSERT_NE(-1, rtp.readFromFile(rtp_fp));
}
// RecOut
WebRtc_Word16 out_data[10 * 32]; // 10 ms at 32 kHz
WebRtc_Word16 out_len = neteq_inst_->recOut(out_data);
ASSERT_TRUE((out_len == 80) || (out_len == 160) || (out_len == 320));
// Read from ref file
WebRtc_Word16 ref_data[10 * 32]; // 10 ms at 32 kHz
if (static_cast<size_t>(out_len) !=
fread(ref_data, sizeof(WebRtc_Word16), out_len, ref_fp)) {
break;
}
// Compare
EXPECT_EQ(0, memcmp(out_data, ref_data, sizeof(WebRtc_Word16) * out_len));
// Increase time
sim_clock += kTimeStep;
}
ASSERT_NE(0, feof(ref_fp)); // Make sure that we reached the end.
fclose(rtp_fp);
fclose(ref_fp);
}
TEST_F(NetEqDecodingTest, TestBitExactness) {
DecodeAndCompare("test/data/audio_coding/universal.rtp",
"test/data/audio_coding/universal_ref.pcm");
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,33 @@
# Copyright (c) 2011 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.
{
'includes': [
'../../../../../common_settings.gypi',
],
'targets': [
{
'target_name': 'neteq_unittest',
'type': 'executable',
'dependencies': [
'neteq.gyp:NetEq',
'neteq.gyp:NetEqTestTools',
'../../../../../../testing/gtest.gyp:gtest',
],
'sources': [
'neteq_api_unittest.cc',
],
},
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -210,7 +210,7 @@ void NETEQTEST_NetEQClass::printError(NETEQTEST_RTPpacket &rtp)
printError();
// print extra info from packet
printf("\tRTP: TS=%lu, SN=%u, PT=%u, M=%i, len=%i\n",
printf("\tRTP: TS=%u, SN=%u, PT=%u, M=%i, len=%i\n",
rtp.timeStamp(), rtp.sequenceNumber(), rtp.payloadType(),
rtp.markerBit(), rtp.payloadLen());

View File

@ -10,6 +10,7 @@
#include "NETEQTEST_RTPpacket.h"
#include <assert.h>
#include <string.h>
#ifdef WIN32
@ -72,7 +73,7 @@ NETEQTEST_RTPpacket & NETEQTEST_RTPpacket::operator = (const NETEQTEST_RTPpacket
// deallocate datagram memory if allocated
if(_datagram)
{
delete[] _datagram;
delete [] _datagram;
}
// do shallow copy
@ -113,7 +114,7 @@ NETEQTEST_RTPpacket::~NETEQTEST_RTPpacket()
{
if(_datagram)
{
delete _datagram;
delete [] _datagram;
}
}
@ -121,7 +122,7 @@ NETEQTEST_RTPpacket::~NETEQTEST_RTPpacket()
void NETEQTEST_RTPpacket::reset()
{
if(_datagram) {
delete _datagram;
delete [] _datagram;
}
_datagram = NULL;
_memSize = 0;
@ -134,6 +135,37 @@ void NETEQTEST_RTPpacket::reset()
}
int NETEQTEST_RTPpacket::skipFileHeader(FILE *fp)
{
if (!fp) {
return -1;
}
const int kFirstLineLength = 40;
char firstline[kFirstLineLength];
fgets(firstline, kFirstLineLength, fp);
if (strncmp(firstline, "#!rtpplay", 9) == 0) {
if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) {
return -1;
}
}
else if (strncmp(firstline, "#!RTPencode", 11) == 0) {
if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) {
return -1;
}
}
else
{
return -1;
}
const int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2;
if (fseek(fp, kRtpDumpHeaderSize, SEEK_CUR) != 0)
{
return -1;
}
return 0;
}
int NETEQTEST_RTPpacket::readFromFile(FILE *fp)
{
@ -201,15 +233,15 @@ int NETEQTEST_RTPpacket::readFromFile(FILE *fp)
}
int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, int length)
int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, size_t length)
{
if(!fp)
if (!fp)
{
return(-1);
return -1;
}
// check buffer size
if (_datagram && _memSize < length)
if (_datagram && _memSize < static_cast<int>(length))
{
reset();
}
@ -220,10 +252,10 @@ int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, int length)
_memSize = length;
}
if (fread((unsigned short *) _datagram,1,length,fp) != length)
if (fread(_datagram, 1, length, fp) != length)
{
reset();
return(-1);
return -1;
}
_datagramLen = length;
@ -232,53 +264,54 @@ int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, int length)
if (!_blockList.empty() && _blockList.count(payloadType()) > 0)
{
// discard this payload
return(readFromFile(fp));
return readFromFile(fp);
}
return(length);
return length;
}
int NETEQTEST_RTPpacket::writeToFile(FILE *fp)
{
if(!fp)
if (!fp)
{
return(-1);
return -1;
}
WebRtc_UWord16 length, plen;
WebRtc_UWord16 length, plen;
WebRtc_UWord32 offset;
// length including RTPplay header
length = htons(_datagramLen + HDR_SIZE);
if (fwrite(&length, 2, 1, fp) != 1)
{
return(-1);
return -1;
}
// payload length
plen = htons(_datagramLen);
if (fwrite(&plen, 2, 1, fp) != 1)
{
return(-1);
return -1;
}
// offset (=receive time)
offset = htonl(_receiveTime);
if (fwrite(&offset, 4, 1, fp) != 1)
{
return(-1);
return -1;
}
// write packet data
if (fwrite((unsigned short *) _datagram, 1, _datagramLen, fp) != _datagramLen)
if (fwrite(_datagram, 1, _datagramLen, fp) !=
static_cast<size_t>(_datagramLen))
{
return(-1);
return -1;
}
return(_datagramLen + HDR_SIZE); // total number of bytes written
return _datagramLen + HDR_SIZE; // total number of bytes written
}
@ -634,6 +667,11 @@ int NETEQTEST_RTPpacket::splitStereo(NETEQTEST_RTPpacket& slaveRtp, enum stereoM
splitStereoFrame(slaveRtp);
break;
}
case stereoModeMono:
{
assert(false);
return -1;
}
}
return 0;

View File

@ -32,8 +32,9 @@ public:
bool operator !() const { return (dataLen() < 0); };
~NETEQTEST_RTPpacket();
void reset();
static int skipFileHeader(FILE *fp);
int readFromFile(FILE *fp);
int readFixedFromFile(FILE *fp, int len);
int readFixedFromFile(FILE *fp, size_t len);
int writeToFile(FILE *fp);
void blockPT(WebRtc_UWord8 pt);
//WebRtc_Word16 payloadType();

View File

@ -150,7 +150,6 @@ int main(int argc, char* argv[])
std::vector<NETEQTEST_NetEQClass *> NetEQvector;
NETEQTEST_RTPpacket rtp;
char version[20];
char firstline[FIRSTLINELEN];
NETEQTEST_RTPpacket slaveRtp;
//bool switchMS = false;
@ -359,40 +358,11 @@ int main(int argc, char* argv[])
/* read RTP file header */
if (!rtpOnly)
{
fgets(firstline, FIRSTLINELEN, in_file);
if(strncmp(firstline,"#!rtpplay",9) == 0) {
if(strncmp(firstline,"#!rtpplay1.0",12) != 0){
printf("ERROR: wrong rtpplay version, must be 1.0\n");
exit(0);
}
if (NETEQTEST_RTPpacket::skipFileHeader(in_file) != 0)
{
fprintf(stderr, "Wrong format in RTP file.\n");
return -1;
}
else if (strncmp(firstline,"#!RTPencode",11) == 0) {
if(strncmp(firstline,"#!RTPencode1.0",14) != 0){
printf("ERROR: wrong RTPencode version, must be 1.0\n");
exit(0);
}
}
else {
printf("ERROR: wrong file format of input file\n");
exit(0);
}
WebRtc_UWord32 start_sec;
WebRtc_UWord32 start_usec;
WebRtc_UWord32 source;
WebRtc_UWord16 port;
WebRtc_UWord16 padding;
fread(&start_sec, 4, 1, in_file);
start_sec=ntohl(start_sec);
fread(&start_usec, 4, 1, in_file);
start_usec=ntohl(start_usec);
fread(&source, 4, 1, in_file);
source=ntohl(source);
fread(&port, 2, 1, in_file);
port=ntohs(port);
fread(&padding, 2, 1, in_file);
padding=ntohs(padding);
}
/* check payload type for first speech packet */
@ -683,14 +653,14 @@ int main(int argc, char* argv[])
WebRtcNetEQ_GetJitterStatistics(NetEQvector[0]->instance(), &jitterStats);
printf("\nPost-call statistics:\n");
printf(" Call duration ms : %lu\n", simClock-start_clock);
printf(" Expand (voice) ms : %lu \t(%.2f%%)\n", jitterStats.interpolatedVoiceMs, (float) 100.0 * jitterStats.interpolatedVoiceMs/(simClock-start_clock));
printf(" Expand (silence) ms : %lu \t(%.2f%%)\n", jitterStats.interpolatedSilentMs, (float) 100.0 * jitterStats.interpolatedSilentMs/(simClock-start_clock));
printf(" Accelerate ms : %lu \t(%.2f%%)\n", jitterStats.accelerateMs, (float) 100.0 * jitterStats.accelerateMs/(simClock-start_clock));
printf(" Flushed ms : %lu \t(%.2f%%)\n", jitterStats.flushedMs, (float) 100.0 * jitterStats.flushedMs/(simClock-start_clock));
printf(" JB avg size ms : %lu\n", jitterStats.jbAvgSize);
printf(" JB max size ms : %lu\n", jitterStats.jbMaxSize);
printf(" Max inter-arrival ms: %lu\n", jitterStats.longestIATms);
printf(" Call duration ms : %u\n", simClock-start_clock);
printf(" Expand (voice) ms : %u \t(%.2f%%)\n", jitterStats.interpolatedVoiceMs, (float) 100.0 * jitterStats.interpolatedVoiceMs/(simClock-start_clock));
printf(" Expand (silence) ms : %u \t(%.2f%%)\n", jitterStats.interpolatedSilentMs, (float) 100.0 * jitterStats.interpolatedSilentMs/(simClock-start_clock));
printf(" Accelerate ms : %u \t(%.2f%%)\n", jitterStats.accelerateMs, (float) 100.0 * jitterStats.accelerateMs/(simClock-start_clock));
printf(" Flushed ms : %u \t(%.2f%%)\n", jitterStats.flushedMs, (float) 100.0 * jitterStats.flushedMs/(simClock-start_clock));
printf(" JB avg size ms : %u\n", jitterStats.jbAvgSize);
printf(" JB max size ms : %u\n", jitterStats.jbMaxSize);
printf(" Max inter-arrival ms: %u\n", jitterStats.longestIATms);
printf("\nComplexity estimates (including sub-components):\n");
printf(" RecIn complexity : %.2f MCPS\n", NetEQvector[0]->getRecInTime() / ((float) 1000*(simClock-start_clock)));

Binary file not shown.

Binary file not shown.