diff --git a/webrtc/modules/audio_coding/neteq/neteq_tests.gypi b/webrtc/modules/audio_coding/neteq/neteq_tests.gypi index 4d2ce252bd..97d835f186 100644 --- a/webrtc/modules/audio_coding/neteq/neteq_tests.gypi +++ b/webrtc/modules/audio_coding/neteq/neteq_tests.gypi @@ -202,6 +202,22 @@ ], }, + { + 'target_name': 'neteq_isac_quality_test', + 'type': 'executable', + 'dependencies': [ + 'neteq', + 'neteq_test_support', + 'iSACFix', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'test/neteq_isac_quality_test.cc', + ], + }, + { 'target_name': 'neteq_test_tools', # Collection of useful functions used in other tests. diff --git a/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc b/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc new file mode 100644 index 0000000000..6b0f48286e --- /dev/null +++ b/webrtc/modules/audio_coding/neteq/test/neteq_isac_quality_test.cc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2014 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 "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" +#include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" +#include "webrtc/test/testsupport/fileutils.h" + +using google::RegisterFlagValidator; +using google::ParseCommandLineFlags; +using std::string; +using testing::InitGoogleTest; + +namespace webrtc { +namespace test { + +static const int kIsacBlockDurationMs = 30; +static const int kIsacInputSamplingKhz = 16; +static const int kIsacOutputSamplingKhz = 16; + +// Define switch for input file name. +static bool ValidateInFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "rb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid input filename."); + return false; +} + +DEFINE_string(in_filename, + ResourcePath("audio_coding/speech_mono_16kHz", "pcm"), + "Filename for input audio (should be 16 kHz sampled mono)."); + +static const bool in_filename_dummy = + RegisterFlagValidator(&FLAGS_in_filename, &ValidateInFilename); + +// Define switch for output file name. +static bool ValidateOutFilename(const char* flagname, const string& value) { + FILE* fid = fopen(value.c_str(), "wb"); + if (fid != NULL) { + fclose(fid); + return true; + } + printf("Invalid output filename."); + return false; +} + +DEFINE_string(out_filename, OutputPath() + "neteq4_isac_quality_test.pcm", + "Name of output audio file."); + +static const bool out_filename_dummy = + RegisterFlagValidator(&FLAGS_out_filename, &ValidateOutFilename); + +// Define switch for bir rate. +static bool ValidateBitRate(const char* flagname, int32_t value) { + if (value >= 10 && value <= 32) + return true; + printf("Invalid bit rate, should be between 10 and 32 kbps."); + return false; +} + +DEFINE_int32(bit_rate_kbps, 32, "Target bit rate (kbps)."); + +static const bool bit_rate_dummy = + RegisterFlagValidator(&FLAGS_bit_rate_kbps, &ValidateBitRate); + +// Define switch for runtime. +static bool ValidateRuntime(const char* flagname, int32_t value) { + if (value > 0) + return true; + printf("Invalid runtime, should be greater than 0."); + return false; +} + +DEFINE_int32(runtime_ms, 10000, "Simulated runtime (milliseconds)."); + +static const bool runtime_dummy = + RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); + +class NetEqIsacQualityTest : public NetEqQualityTest { + protected: + NetEqIsacQualityTest(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; + virtual int EncodeBlock(int16_t* in_data, int block_size_samples, + uint8_t* payload, int max_bytes); + private: + ISACFIX_MainStruct* isac_encoder_; + int bit_rate_kbps_; +}; + +NetEqIsacQualityTest::NetEqIsacQualityTest() + : NetEqQualityTest(kIsacBlockDurationMs, kIsacInputSamplingKhz, + kIsacOutputSamplingKhz, + kDecoderISAC, + 1, + FLAGS_in_filename, + FLAGS_out_filename), + isac_encoder_(NULL), + bit_rate_kbps_(FLAGS_bit_rate_kbps) { +} + +void NetEqIsacQualityTest::SetUp() { + // Create encoder memory. + WebRtcIsacfix_Create(&isac_encoder_); + ASSERT_TRUE(isac_encoder_ != NULL); + EXPECT_EQ(0, WebRtcIsacfix_EncoderInit(isac_encoder_, 1)); + // Set bitrate and block length. + EXPECT_EQ(0, WebRtcIsacfix_Control(isac_encoder_, bit_rate_kbps_ * 1000, + kIsacBlockDurationMs)); + NetEqQualityTest::SetUp(); +} + +void NetEqIsacQualityTest::TearDown() { + // Free memory. + EXPECT_EQ(0, WebRtcIsacfix_Free(isac_encoder_)); + NetEqQualityTest::TearDown(); +} + +int NetEqIsacQualityTest::EncodeBlock(int16_t* in_data, + int block_size_samples, + uint8_t* payload, int max_bytes) { + // ISAC takes 10 ms for every call. + const int subblocks = kIsacBlockDurationMs / 10; + const int subblock_length = 10 * kIsacInputSamplingKhz; + int value = 0; + + int pointer = 0; + for (int idx = 0; idx < subblocks; idx++, pointer += subblock_length) { + // The Isac encoder does not perform encoding (and returns 0) until it + // receives a sequence of sub-blocks that amount to the frame duration. + EXPECT_EQ(0, value); + value = WebRtcIsacfix_Encode(isac_encoder_, &in_data[pointer], + reinterpret_cast(payload)); + } + EXPECT_GT(value, 0); + return value; +} + +TEST_F(NetEqIsacQualityTest, Test) { + Simulate(FLAGS_runtime_ms); +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc b/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc index ad6d8ece45..e8fd06a459 100644 --- a/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc +++ b/webrtc/modules/audio_coding/neteq/test/neteq_opus_fec_quality_test.cc @@ -8,7 +8,6 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include #include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" #include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" #include "webrtc/test/testsupport/fileutils.h" @@ -25,6 +24,7 @@ static const int kOpusBlockDurationMs = 20; static const int kOpusInputSamplingKhz = 48; static const int kOpusOutputSamplingKhz = 32; +// Define switch for input file name. static bool ValidateInFilename(const char* flagname, const string& value) { FILE* fid = fopen(value.c_str(), "rb"); if (fid != NULL) { @@ -34,12 +34,15 @@ static bool ValidateInFilename(const char* flagname, const string& value) { printf("Invalid input filename."); return false; } + DEFINE_string(in_filename, ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm"), "Filename for input audio (should be 48 kHz sampled raw data)."); + static const bool in_filename_dummy = RegisterFlagValidator(&FLAGS_in_filename, &ValidateInFilename); +// Define switch for output file name. static bool ValidateOutFilename(const char* flagname, const string& value) { FILE* fid = fopen(value.c_str(), "wb"); if (fid != NULL) { @@ -49,50 +52,60 @@ static bool ValidateOutFilename(const char* flagname, const string& value) { printf("Invalid output filename."); return false; } + DEFINE_string(out_filename, OutputPath() + "neteq4_opus_fec_quality_test.pcm", "Name of output audio file."); + static const bool out_filename_dummy = RegisterFlagValidator(&FLAGS_out_filename, &ValidateOutFilename); +// Define switch for channels. static bool ValidateChannels(const char* flagname, int32_t value) { if (value == 1 || value == 2) return true; printf("Invalid number of channels, should be either 1 or 2."); return false; } + DEFINE_int32(channels, 1, "Number of channels in input audio."); + static const bool channels_dummy = RegisterFlagValidator(&FLAGS_channels, &ValidateChannels); +// Define switch for bit rate. static bool ValidateBitRate(const char* flagname, int32_t value) { if (value >= 6 && value <= 510) return true; printf("Invalid bit rate, should be between 6 and 510 kbps."); return false; } + DEFINE_int32(bit_rate_kbps, 32, "Target bit rate (kbps)."); + static const bool bit_rate_dummy = RegisterFlagValidator(&FLAGS_bit_rate_kbps, &ValidateBitRate); +// Define switch for reported packet loss rate. static bool ValidatePacketLossRate(const char* flagname, int32_t value) { if (value >= 0 && value <= 100) return true; printf("Invalid packet loss percentile, should be between 0 and 100."); return false; } + DEFINE_int32(reported_loss_rate, 10, "Reported percentile of packet loss."); + static const bool reported_loss_rate_dummy = RegisterFlagValidator(&FLAGS_reported_loss_rate, &ValidatePacketLossRate); -DEFINE_int32(actual_loss_rate, 0, "Actual percentile of packet loss."); -static const bool actual_loss_rate_dummy = - RegisterFlagValidator(&FLAGS_actual_loss_rate, &ValidatePacketLossRate); +// Define switch for runtime. static bool ValidateRuntime(const char* flagname, int32_t value) { if (value > 0) return true; printf("Invalid runtime, should be greater than 0."); return false; } + DEFINE_int32(runtime_ms, 10000, "Simulated runtime (milliseconds)."); static const bool runtime_dummy = RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); @@ -106,28 +119,26 @@ class NetEqOpusFecQualityTest : public NetEqQualityTest { virtual void TearDown() OVERRIDE; virtual int EncodeBlock(int16_t* in_data, int block_size_samples, uint8_t* payload, int max_bytes); - virtual bool PacketLost(int packet_input_time_ms); private: WebRtcOpusEncInst* opus_encoder_; int channels_; int bit_rate_kbps_; bool fec_; int target_loss_rate_; - int actual_loss_rate_; }; NetEqOpusFecQualityTest::NetEqOpusFecQualityTest() : NetEqQualityTest(kOpusBlockDurationMs, kOpusInputSamplingKhz, kOpusOutputSamplingKhz, (FLAGS_channels == 1) ? kDecoderOpus : kDecoderOpus_2ch, - FLAGS_channels, 0.0f, FLAGS_in_filename, + FLAGS_channels, + FLAGS_in_filename, FLAGS_out_filename), opus_encoder_(NULL), channels_(FLAGS_channels), bit_rate_kbps_(FLAGS_bit_rate_kbps), fec_(FLAGS_fec), - target_loss_rate_(FLAGS_reported_loss_rate), - actual_loss_rate_(FLAGS_actual_loss_rate) { + target_loss_rate_(FLAGS_reported_loss_rate) { } void NetEqOpusFecQualityTest::SetUp() { @@ -138,9 +149,9 @@ void NetEqOpusFecQualityTest::SetUp() { EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_kbps_ * 1000)); if (fec_) { EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_)); - EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, - target_loss_rate_)); } + EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, + target_loss_rate_)); NetEqQualityTest::SetUp(); } @@ -160,16 +171,6 @@ int NetEqOpusFecQualityTest::EncodeBlock(int16_t* in_data, return value; } -bool NetEqOpusFecQualityTest::PacketLost(int packet_input_time_ms) { - static int packets = 0, lost_packets = 0; - packets++; - if (lost_packets * 100 < actual_loss_rate_ * packets) { - lost_packets++; - return true; - } - return false; -} - TEST_F(NetEqOpusFecQualityTest, Test) { Simulate(FLAGS_runtime_ms); } diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc index fc5d8abaf8..a80b1f887a 100644 --- a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc +++ b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.cc @@ -8,6 +8,7 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include #include #include "webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h" @@ -16,18 +17,116 @@ namespace test { const uint8_t kPayloadType = 95; const int kOutputSizeMs = 10; +const int kInitSeed = 0x12345678; +const int kPacketLossTimeUnitMs = 10; + +// Define switch for packet loss rate. +static bool ValidatePacketLossRate(const char* /* flag_name */, int32_t value) { + if (value >= 0 && value <= 100) + return true; + printf("Invalid packet loss percentile, should be between 0 and 100."); + return false; +} + +DEFINE_int32(packet_loss_rate, 10, "Percentile of packet loss."); + +static const bool packet_loss_rate_dummy = + RegisterFlagValidator(&FLAGS_packet_loss_rate, &ValidatePacketLossRate); + +// Define switch for random loss mode. +static bool ValidateRandomLossMode(const char* /* flag_name */, int32_t value) { + if (value >= 0 && value <= 2) + return true; + printf("Invalid random packet loss mode, should be between 0 and 2."); + return false; +} + +DEFINE_int32(random_loss_mode, 1, + "Random loss mode: 0--no loss, 1--uniform loss, 2--Gilbert Elliot loss."); +static const bool random_loss_mode_dummy = + RegisterFlagValidator(&FLAGS_random_loss_mode, &ValidateRandomLossMode); + +// Define switch for burst length. +static bool ValidateBurstLength(const char* /* flag_name */, int32_t value) { + if (value >= kPacketLossTimeUnitMs) + return true; + printf("Invalid burst length, should be greater than %d ms.", + kPacketLossTimeUnitMs); + return false; +} + +DEFINE_int32(burst_length, 30, + "Burst length in milliseconds, only valid for Gilbert Elliot loss."); + +static const bool burst_length_dummy = + RegisterFlagValidator(&FLAGS_burst_length, &ValidateBurstLength); + +// Define switch for drift factor. +static bool ValidateDriftFactor(const char* /* flag_name */, double value) { + if (value > -0.1) + return true; + printf("Invalid drift factor, should be greater than -0.1."); + return false; +} + +DEFINE_double(drift_factor, 0.0, "Time drift factor."); + +static const bool drift_factor_dummy = + RegisterFlagValidator(&FLAGS_drift_factor, &ValidateDriftFactor); + +// ProbTrans00Solver() is to calculate the transition probability from no-loss +// state to itself in a modified Gilbert Elliot packet loss model. The result is +// to achieve the target packet loss rate |loss_rate|, when a packet is not +// lost only if all |units| drawings within the duration of the packet result in +// no-loss. +static double ProbTrans00Solver(int units, double loss_rate, + double prob_trans_10) { + if (units == 1) + return prob_trans_10 / (1.0f - loss_rate) - prob_trans_10; +// 0 == prob_trans_00 ^ (units - 1) + (1 - loss_rate) / prob_trans_10 * +// prob_trans_00 - (1 - loss_rate) * (1 + 1 / prob_trans_10). +// There is a unique solution between 0.0 and 1.0, due to the monotonicity and +// an opposite sign at 0.0 and 1.0. +// For simplicity, we reformulate the equation as +// f(x) = x ^ (units - 1) + a x + b. +// Its derivative is +// f'(x) = (units - 1) x ^ (units - 2) + a. +// The derivative is strictly greater than 0 when x is between 0 and 1. +// We use Newton's method to solve the equation, iteration is +// x(k+1) = x(k) - f(x) / f'(x); + const double kPrecision = 0.001f; + const int kIterations = 100; + const double a = (1.0f - loss_rate) / prob_trans_10; + const double b = (loss_rate - 1.0f) * (1.0f + 1.0f / prob_trans_10); + double x = 0.0f; // Starting point; + double f = b; + double f_p; + int iter = 0; + while ((f >= kPrecision || f <= -kPrecision) && iter < kIterations) { + f_p = (units - 1.0f) * pow(x, units - 2) + a; + x -= f / f_p; + if (x > 1.0f) { + x = 1.0f; + } else if (x < 0.0f) { + x = 0.0f; + } + f = pow(x, units - 1) + a * x + b; + iter ++; + } + return x; +} NetEqQualityTest::NetEqQualityTest(int block_duration_ms, int in_sampling_khz, int out_sampling_khz, enum NetEqDecoder decoder_type, int channels, - double drift_factor, std::string in_filename, std::string out_filename) : decoded_time_ms_(0), decodable_time_ms_(0), - drift_factor_(drift_factor), + drift_factor_(FLAGS_drift_factor), + packet_loss_rate_(FLAGS_packet_loss_rate), block_duration_ms_(block_duration_ms), in_sampling_khz_(in_sampling_khz), out_sampling_khz_(out_sampling_khz), @@ -35,14 +134,17 @@ NetEqQualityTest::NetEqQualityTest(int block_duration_ms, channels_(channels), in_filename_(in_filename), out_filename_(out_filename), + log_filename_(out_filename + ".log"), in_size_samples_(in_sampling_khz_ * block_duration_ms_), out_size_samples_(out_sampling_khz_ * kOutputSizeMs), payload_size_bytes_(0), max_payload_bytes_(0), in_file_(new InputAudioFile(in_filename_)), out_file_(NULL), + log_file_(NULL), rtp_generator_(new RtpGenerator(in_sampling_khz_, 0, 0, - decodable_time_ms_)) { + decodable_time_ms_)), + total_payload_size_bytes_(0) { NetEq::Config config; config.sample_rate_hz = out_sampling_khz_ * 1000; neteq_.reset(NetEq::Create(config)); @@ -52,27 +154,136 @@ NetEqQualityTest::NetEqQualityTest(int block_duration_ms, out_data_.reset(new int16_t[out_size_samples_ * channels_]); } +bool NoLoss::Lost() { + return false; +} + +UniformLoss::UniformLoss(int loss_rate) + : loss_rate_(loss_rate) { +} + +bool UniformLoss::Lost() { + int drop_this = rand(); + return (drop_this < loss_rate_ * RAND_MAX); +} + +GilbertElliotLoss::GilbertElliotLoss(double prob_trans_11, double prob_trans_01) + : prob_trans_11_(prob_trans_11), + prob_trans_01_(prob_trans_01), + lost_last_(false), + uniform_loss_model_(new UniformLoss(0)) { +} + +bool GilbertElliotLoss::Lost() { + // Simulate bursty channel (Gilbert model). + // (1st order) Markov chain model with memory of the previous/last + // packet state (lost or received). + if (lost_last_) { + // Previous packet was not received. + uniform_loss_model_->set_loss_rate(prob_trans_11_); + return lost_last_ = uniform_loss_model_->Lost(); + } else { + uniform_loss_model_->set_loss_rate(prob_trans_01_); + return lost_last_ = uniform_loss_model_->Lost(); + } +} + void NetEqQualityTest::SetUp() { out_file_ = fopen(out_filename_.c_str(), "wb"); + log_file_ = fopen(log_filename_.c_str(), "wt"); ASSERT_TRUE(out_file_ != NULL); ASSERT_EQ(0, neteq_->RegisterPayloadType(decoder_type_, kPayloadType)); rtp_generator_->set_drift_factor(drift_factor_); + + int units = block_duration_ms_ / kPacketLossTimeUnitMs; + switch (FLAGS_random_loss_mode) { + case 1: { + // |unit_loss_rate| is the packet loss rate for each unit time interval + // (kPacketLossTimeUnitMs). Since a packet loss event is generated if any + // of |block_duration_ms_ / kPacketLossTimeUnitMs| unit time intervals of + // a full packet duration is drawn with a loss, |unit_loss_rate| fulfills + // (1 - unit_loss_rate) ^ (block_duration_ms_ / kPacketLossTimeUnitMs) == + // 1 - packet_loss_rate. + // |unit_loss_rate| is usually small. To increase its resolution, we + // magnify it by |RAND_MAX|. + double unit_loss_rate = (1.0f - pow(1.0f - 0.01f * packet_loss_rate_, + 1.0f / units)); + loss_model_.reset(new UniformLoss(unit_loss_rate)); + break; + } + case 2: { + // |FLAGS_burst_length| should be integer times of kPacketLossTimeUnitMs. + ASSERT_EQ(0, FLAGS_burst_length % kPacketLossTimeUnitMs); + + // We do not allow 100 percent packet loss in Gilbert Elliot model, which + // makes no sense. + ASSERT_GT(100, packet_loss_rate_); + + // To guarantee the overall packet loss rate, transition probabilities + // need to satisfy: + // pi_0 * (1 - prob_trans_01_) ^ units + + // pi_1 * prob_trans_10_ ^ (units - 1) == 1 - loss_rate + // pi_0 = prob_trans_10 / (prob_trans_10 + prob_trans_01_) + // is the stationary state probability of no-loss + // pi_1 = prob_trans_01_ / (prob_trans_10 + prob_trans_01_) + // is the stationary state probability of loss + // After a derivation prob_trans_00 should satisfy: + // prob_trans_00 ^ (units - 1) = (loss_rate - 1) / prob_trans_10 * + // prob_trans_00 + (1 - loss_rate) * (1 + 1 / prob_trans_10). + double loss_rate = 0.01f * packet_loss_rate_; + double prob_trans_10 = 1.0f * kPacketLossTimeUnitMs / FLAGS_burst_length; + double prob_trans_00 = ProbTrans00Solver(units, loss_rate, prob_trans_10); + loss_model_.reset(new GilbertElliotLoss(1.0f - prob_trans_10, + 1.0f - prob_trans_00)); + break; + } + default: { + loss_model_.reset(new NoLoss); + break; + } + } + + // Make sure that the packet loss profile is same for all derived tests. + srand(kInitSeed); } void NetEqQualityTest::TearDown() { fclose(out_file_); } +bool NetEqQualityTest::PacketLost() { + int cycles = block_duration_ms_ / kPacketLossTimeUnitMs; + + // The loop is to make sure that codecs with different block lengths share the + // same packet loss profile. + bool lost = false; + for (int idx = 0; idx < cycles; idx ++) { + if (loss_model_->Lost()) { + // The packet will be lost if any of the drawings indicates a loss, but + // the loop has to go on to make sure that codecs with different block + // lengths keep the same pace. + lost = true; + } + } + return lost; +} + int NetEqQualityTest::Transmit() { int packet_input_time_ms = rtp_generator_->GetRtpHeader(kPayloadType, in_size_samples_, &rtp_header_); - if (!PacketLost(packet_input_time_ms) && payload_size_bytes_ > 0) { - int ret = neteq_->InsertPacket(rtp_header_, &payload_[0], - payload_size_bytes_, - packet_input_time_ms * in_sampling_khz_); - if (ret != NetEq::kOK) - return -1; + if (payload_size_bytes_ > 0) { + fprintf(log_file_, "Packet at %d ms", packet_input_time_ms); + if (!PacketLost()) { + int ret = neteq_->InsertPacket(rtp_header_, &payload_[0], + payload_size_bytes_, + packet_input_time_ms * in_sampling_khz_); + if (ret != NetEq::kOK) + return -1; + fprintf(log_file_, " OK.\n"); + } else { + fprintf(log_file_, " Lost.\n"); + } } return packet_input_time_ms; } @@ -97,11 +308,13 @@ void NetEqQualityTest::Simulate(int end_time_ms) { int audio_size_samples; while (decoded_time_ms_ < end_time_ms) { - while (decodable_time_ms_ - kOutputSizeMs < decoded_time_ms_) { + // Assume 10 packets in packets buffer. + while (decodable_time_ms_ - 10 * block_duration_ms_ < decoded_time_ms_) { ASSERT_TRUE(in_file_->Read(in_size_samples_ * channels_, &in_data_[0])); payload_size_bytes_ = EncodeBlock(&in_data_[0], in_size_samples_, &payload_[0], max_payload_bytes_); + total_payload_size_bytes_ += payload_size_bytes_; decodable_time_ms_ = Transmit() + block_duration_ms_; } audio_size_samples = DecodeBlock(); @@ -109,6 +322,7 @@ void NetEqQualityTest::Simulate(int end_time_ms) { decoded_time_ms_ += audio_size_samples / out_sampling_khz_; } } + fprintf(log_file_, "%f", 8.0f * total_payload_size_bytes_ / end_time_ms); } } // namespace test diff --git a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h index 87fc50794c..75d19ae6cf 100644 --- a/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h +++ b/webrtc/modules/audio_coding/neteq/tools/neteq_quality_test.h @@ -11,6 +11,7 @@ #ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_ #define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_NETEQ_QUALITY_TEST_H_ +#include #include #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/modules/audio_coding/neteq/interface/neteq.h" @@ -19,9 +20,44 @@ #include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" +using google::RegisterFlagValidator; + namespace webrtc { namespace test { +class LossModel { + public: + virtual ~LossModel() {}; + virtual bool Lost() = 0; +}; + +class NoLoss : public LossModel { + public: + virtual bool Lost() OVERRIDE; +}; + +class UniformLoss : public LossModel { + public: + UniformLoss(int loss_rate); + virtual bool Lost() OVERRIDE; + void set_loss_rate(double loss_rate) { loss_rate_ = loss_rate; } + private: + double loss_rate_; +}; + +class GilbertElliotLoss : public LossModel { + public: + GilbertElliotLoss(double prob_trans_11, double prob_trans_01); + virtual bool Lost() OVERRIDE; + private: + // Prob. of losing current packet, when previous packet is lost. + double prob_trans_11_; + // Prob. of losing current packet, when previous packet is not lost. + double prob_trans_01_; + bool lost_last_; + scoped_ptr uniform_loss_model_; +}; + class NetEqQualityTest : public ::testing::Test { protected: NetEqQualityTest(int block_duration_ms, @@ -29,7 +65,6 @@ class NetEqQualityTest : public ::testing::Test { int out_sampling_khz, enum NetEqDecoder decoder_type, int channels, - double drift_factor, std::string in_filename, std::string out_filename); virtual void SetUp() OVERRIDE; @@ -43,9 +78,9 @@ class NetEqQualityTest : public ::testing::Test { virtual int EncodeBlock(int16_t* in_data, int block_size_samples, uint8_t* payload, int max_bytes) = 0; - // PacketLoss(...) determines weather a packet sent at an indicated time gets + // PacketLost(...) determines weather a packet sent at an indicated time gets // lost or not. - virtual bool PacketLost(int packet_input_time_ms) { return false; } + bool PacketLost(); // DecodeBlock() decodes a block of audio using the payload stored in // |payload_| with the length of |payload_size_bytes_| (bytes). The decoded @@ -65,6 +100,7 @@ class NetEqQualityTest : public ::testing::Test { int decoded_time_ms_; int decodable_time_ms_; double drift_factor_; + int packet_loss_rate_; const int block_duration_ms_; const int in_sampling_khz_; const int out_sampling_khz_; @@ -72,6 +108,7 @@ class NetEqQualityTest : public ::testing::Test { const int channels_; const std::string in_filename_; const std::string out_filename_; + const std::string log_filename_; // Number of samples per channel in a frame. const int in_size_samples_; @@ -84,14 +121,18 @@ class NetEqQualityTest : public ::testing::Test { scoped_ptr in_file_; FILE* out_file_; + FILE* log_file_; scoped_ptr rtp_generator_; scoped_ptr neteq_; + scoped_ptr loss_model_; scoped_ptr in_data_; scoped_ptr payload_; scoped_ptr out_data_; WebRtcRTPHeader rtp_header_; + + long total_payload_size_bytes_; }; } // namespace test