diff --git a/src/modules/audio_coding/neteq/webrtc_neteq_unittest.cc b/src/modules/audio_coding/neteq/webrtc_neteq_unittest.cc index b6b1082e6c..45f5b9613b 100644 --- a/src/modules/audio_coding/neteq/webrtc_neteq_unittest.cc +++ b/src/modules/audio_coding/neteq/webrtc_neteq_unittest.cc @@ -12,7 +12,6 @@ * This file includes unit tests for NetEQ. */ -#include #include #include // memset @@ -21,31 +20,182 @@ #include "gtest/gtest.h" +#include "modules/audio_coding/neteq/interface/webrtc_neteq.h" +#include "modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" #include "modules/audio_coding/neteq/test/NETEQTEST_CodecClass.h" #include "modules/audio_coding/neteq/test/NETEQTEST_NetEQClass.h" #include "modules/audio_coding/neteq/test/NETEQTEST_RTPpacket.h" -#include "typedefs.h" // NOLINT(build/include) -#include "modules/audio_coding/neteq/interface/webrtc_neteq.h" -#include "modules/audio_coding/neteq/interface/webrtc_neteq_help_macros.h" #include "testsupport/fileutils.h" +#include "typedefs.h" // NOLINT(build/include) namespace webrtc { +class RefFiles { + public: + RefFiles(const std::string& input_file, const std::string& output_file); + ~RefFiles(); + template void ProcessReference(const T& test_results); + template void ProcessReference( + const T (&test_results)[n], + size_t length); + template void WriteToFile( + const T (&test_results)[n], + size_t length); + template void ReadFromFileAndCompare( + const T (&test_results)[n], + size_t length); + void WriteToFile(const WebRtcNetEQ_NetworkStatistics& stats); + void ReadFromFileAndCompare(const WebRtcNetEQ_NetworkStatistics& stats); + void WriteToFile(const WebRtcNetEQ_RTCPStat& stats); + void ReadFromFileAndCompare(const WebRtcNetEQ_RTCPStat& stats); + + FILE* input_fp_; + FILE* output_fp_; +}; + +RefFiles::RefFiles(const std::string &input_file, + const std::string &output_file) + : input_fp_(NULL), + output_fp_(NULL) { + if (!input_file.empty()) { + input_fp_ = fopen(input_file.c_str(), "rb"); + EXPECT_TRUE(input_fp_ != NULL); + } + if (!output_file.empty()) { + output_fp_ = fopen(output_file.c_str(), "wb"); + EXPECT_TRUE(output_fp_ != NULL); + } +} + +RefFiles::~RefFiles() { + if (input_fp_) { + EXPECT_EQ(EOF, fgetc(input_fp_)); // Make sure that we reached the end. + fclose(input_fp_); + } + if (output_fp_) fclose(output_fp_); +} + +template +void RefFiles::ProcessReference(const T& test_results) { + WriteToFile(test_results); + ReadFromFileAndCompare(test_results); +} + +template +void RefFiles::ProcessReference(const T (&test_results)[n], size_t length) { + WriteToFile(test_results, length); + ReadFromFileAndCompare(test_results, length); +} + +template +void RefFiles::WriteToFile(const T (&test_results)[n], size_t length) { + if (output_fp_) { + ASSERT_EQ(length, fwrite(&test_results, sizeof(T), length, output_fp_)); + } +} + +template +void RefFiles::ReadFromFileAndCompare(const T (&test_results)[n], + size_t length) { + if (input_fp_) { + // Read from ref file. + T* ref = new T[length]; + ASSERT_EQ(length, fread(ref, sizeof(T), length, input_fp_)); + // Compare + EXPECT_EQ(0, memcmp(&test_results, ref, sizeof(T) * length)); + delete [] ref; + } +} + +void RefFiles::WriteToFile(const WebRtcNetEQ_NetworkStatistics& stats) { + if (output_fp_) { + ASSERT_EQ(1u, fwrite(&stats, sizeof(WebRtcNetEQ_NetworkStatistics), 1, + output_fp_)); + } +} + +void RefFiles::ReadFromFileAndCompare( + const WebRtcNetEQ_NetworkStatistics& stats) { + if (input_fp_) { + // Read from ref file. + size_t stat_size = sizeof(WebRtcNetEQ_NetworkStatistics); + WebRtcNetEQ_NetworkStatistics ref_stats; + ASSERT_EQ(1u, fread(&ref_stats, stat_size, 1, input_fp_)); + // Compare + EXPECT_EQ(0, memcmp(&stats, &ref_stats, stat_size)); + } +} + +void RefFiles::WriteToFile(const WebRtcNetEQ_RTCPStat& stats) { + if (output_fp_) { + ASSERT_EQ(1u, fwrite(&(stats.fraction_lost), sizeof(stats.fraction_lost), 1, + output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.cum_lost), sizeof(stats.cum_lost), 1, + output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.ext_max), sizeof(stats.ext_max), 1, + output_fp_)); + ASSERT_EQ(1u, fwrite(&(stats.jitter), sizeof(stats.jitter), 1, + output_fp_)); + } +} + +void RefFiles::ReadFromFileAndCompare( + const WebRtcNetEQ_RTCPStat& stats) { + if (input_fp_) { + // Read from ref file. + WebRtcNetEQ_RTCPStat ref_stats; + ASSERT_EQ(1u, fread(&(ref_stats.fraction_lost), + sizeof(ref_stats.fraction_lost), 1, input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.cum_lost), sizeof(ref_stats.cum_lost), 1, + input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.ext_max), sizeof(ref_stats.ext_max), 1, + input_fp_)); + ASSERT_EQ(1u, fread(&(ref_stats.jitter), sizeof(ref_stats.jitter), 1, + input_fp_)); + // Compare + EXPECT_EQ(ref_stats.fraction_lost, stats.fraction_lost); + EXPECT_EQ(ref_stats.cum_lost, stats.cum_lost); + EXPECT_EQ(ref_stats.ext_max, stats.ext_max); + EXPECT_EQ(ref_stats.jitter, stats.jitter); + } +} + class NetEqDecodingTest : public ::testing::Test { protected: + // NetEQ must be polled for data once every 10 ms. Thus, neither of the + // constants below can be changed. + static const int kTimeStepMs = 10; + static const int kBlockSize8kHz = kTimeStepMs * 8; + static const int kBlockSize16kHz = kTimeStepMs * 16; + static const int kBlockSize32kHz = kTimeStepMs * 32; + static const int kMaxBlockSize = kBlockSize32kHz; + NetEqDecodingTest(); virtual void SetUp(); virtual void TearDown(); void SelectDecoders(WebRtcNetEQDecoder* used_codec); void LoadDecoders(); + void OpenInputFile(const std::string &rtp_file); + void Process(NETEQTEST_RTPpacket* rtp_ptr, int16_t* out_len); void DecodeAndCompare(const std::string &rtp_file, const std::string &ref_file); + void DecodeAndCheckStats(const std::string &rtp_file, + const std::string &stat_ref_file, + const std::string &rtcp_ref_file); NETEQTEST_NetEQClass* neteq_inst_; std::vector dec_; + FILE* rtp_fp_; + unsigned int sim_clock_; + int16_t out_data_[kMaxBlockSize]; }; -NetEqDecodingTest::NetEqDecodingTest() : neteq_inst_(NULL) {} +NetEqDecodingTest::NetEqDecodingTest() + : neteq_inst_(NULL), + rtp_fp_(NULL), + sim_clock_(0) { + memset(out_data_, 0, sizeof(out_data_)); +} void NetEqDecodingTest::SetUp() { WebRtcNetEQDecoder usedCodec[kDecoderReservedEnd - 1]; @@ -64,6 +214,8 @@ void NetEqDecodingTest::TearDown() { if (dec_[i]) delete dec_[i]; } + if (rtp_fp_) + fclose(rtp_fp_); } void NetEqDecodingTest::SelectDecoders(WebRtcNetEQDecoder* used_codec) { @@ -93,78 +245,91 @@ void NetEqDecodingTest::LoadDecoders() { } } +void NetEqDecodingTest::OpenInputFile(const std::string &rtp_file) { + rtp_fp_ = fopen(rtp_file.c_str(), "rb"); + ASSERT_TRUE(rtp_fp_ != NULL); + ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp_)); +} + +void NetEqDecodingTest::Process(NETEQTEST_RTPpacket* rtp, int16_t* out_len) { + // 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 + *out_len = neteq_inst_->recOut(out_data_); + ASSERT_TRUE((*out_len == kBlockSize8kHz) || + (*out_len == kBlockSize16kHz) || + (*out_len == kBlockSize32kHz)); + + // Increase time. + sim_clock_ += kTimeStepMs; +} + void NetEqDecodingTest::DecodeAndCompare(const std::string &rtp_file, const std::string &ref_file) { + OpenInputFile(rtp_file); + + std::string ref_out_file = ""; + if (ref_file.empty()) { + ref_out_file = webrtc::test::OutputPath() + "neteq_out.pcm"; + } + RefFiles ref_files(ref_file, ref_out_file); + NETEQTEST_RTPpacket rtp; - FILE* rtp_fp = fopen(rtp_file.c_str(), "rb"); - ASSERT_TRUE(rtp_fp != NULL); - ASSERT_EQ(0, NETEQTEST_RTPpacket::skipFileHeader(rtp_fp)); - ASSERT_GT(rtp.readFromFile(rtp_fp), 0); - - FILE* ref_fp = NULL; - FILE* out_fp = NULL; - if (!ref_file.empty()) { - ref_fp = fopen(ref_file.c_str(), "rb"); - ASSERT_TRUE(ref_fp != NULL); - } else { - std::string out_file = webrtc::test::OutputPath() + "neteq_out.pcm"; - out_fp = fopen(out_file.c_str(), "wb"); - ASSERT_TRUE(out_fp != NULL); - } - - unsigned int sim_clock = 0; - // NetEQ must be polled for data once every 10 ms. Thus, neither of the - // constants below can be changed. - const int kTimeStepMs = 10; - const int kBlockSize8kHz = kTimeStepMs * 8; - const int kBlockSize16kHz = kTimeStepMs * 16; - const int kBlockSize32kHz = kTimeStepMs * 32; - const int kMaxBlockSize = kBlockSize32kHz; + ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); 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[kMaxBlockSize]; - WebRtc_Word16 out_len = neteq_inst_->recOut(out_data); - ASSERT_TRUE((out_len == kBlockSize8kHz) || - (out_len == kBlockSize16kHz) || - (out_len == kBlockSize32kHz)); - - if (ref_fp) { - // Read from ref file. - WebRtc_Word16 ref_data[kMaxBlockSize]; - if (static_cast(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)); - } - - if (out_fp) { - // Write to output file (mainly for generating new output vectors). - ASSERT_EQ(static_cast(out_len), - fwrite(out_data, sizeof(WebRtc_Word16), out_len, out_fp)); - } - - // Increase time. - sim_clock += kTimeStepMs; + int16_t out_len; + Process(&rtp, &out_len); + ref_files.ProcessReference(out_data_, out_len); } - fclose(rtp_fp); - if (ref_fp) { - ASSERT_NE(0, feof(ref_fp)); // Make sure that we reached the end. - fclose(ref_fp); +} + +void NetEqDecodingTest::DecodeAndCheckStats(const std::string &rtp_file, + const std::string &stat_ref_file, + const std::string &rtcp_ref_file) { + OpenInputFile(rtp_file); + std::string stat_out_file = ""; + if (stat_ref_file.empty()) { + stat_out_file = webrtc::test::OutputPath() + + "neteq_network_stats.dat"; + } + RefFiles network_stat_files(stat_ref_file, stat_out_file); + + std::string rtcp_out_file = ""; + if (rtcp_ref_file.empty()) { + rtcp_out_file = webrtc::test::OutputPath() + + "neteq_rtcp_stats.dat"; + } + RefFiles rtcp_stat_files(rtcp_ref_file, rtcp_out_file); + + NETEQTEST_RTPpacket rtp; + ASSERT_GT(rtp.readFromFile(rtp_fp_), 0); + while (rtp.dataLen() >= 0) { + int16_t out_len; + Process(&rtp, &out_len); + + // Query the network statistics API once per second + if (sim_clock_ % 1000 == 0) { + // Process NetworkStatistics. + WebRtcNetEQ_NetworkStatistics network_stats; + ASSERT_EQ(0, WebRtcNetEQ_GetNetworkStatistics(neteq_inst_->instance(), + &network_stats)); + network_stat_files.ProcessReference(network_stats); + + // Process RTCPstat. + WebRtcNetEQ_RTCPStat rtcp_stats; + ASSERT_EQ(0, WebRtcNetEQ_GetRTCPStats(neteq_inst_->instance(), + &rtcp_stats)); + rtcp_stat_files.ProcessReference(rtcp_stats); + } } - if (out_fp) fclose(out_fp); } #if defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_64_BITS) @@ -177,4 +342,6 @@ TEST_F(NetEqDecodingTest, TestBitExactness) { } #endif // defined(WEBRTC_LINUX) && defined(WEBRTC_ARCH_64_BITS) +//TODO(hlundin): Add test target NetEqDecodingTest::TestNetworkStatistics. + } // namespace