From d1fc5d4e179d162cf9453f23451793c71cab1609 Mon Sep 17 00:00:00 2001 From: "henrik.lundin@webrtc.org" Date: Tue, 17 Sep 2013 08:38:02 +0000 Subject: [PATCH] Dedicated speed test for NetEq4 This CL implements a new speed test application for NetEq4. The application runs a minimum of overhead in order to highlight the performance of NetEq itself. BUG=1363 R=minyue@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2177006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4763 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/modules/audio_coding/neteq4/neteq.gypi | 2 + .../audio_coding/neteq4/neteq_tests.gypi | 14 ++ .../neteq4/test/neteq_speed_test.cc | 185 ++++++++++++++++++ .../audio_coding/neteq4/tools/audio_loop.cc | 57 ++++++ .../audio_coding/neteq4/tools/audio_loop.h | 60 ++++++ 5 files changed, 318 insertions(+) create mode 100644 webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc create mode 100644 webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc create mode 100644 webrtc/modules/audio_coding/neteq4/tools/audio_loop.h diff --git a/webrtc/modules/audio_coding/neteq4/neteq.gypi b/webrtc/modules/audio_coding/neteq4/neteq.gypi index 936ab04f3..b543e4857 100644 --- a/webrtc/modules/audio_coding/neteq4/neteq.gypi +++ b/webrtc/modules/audio_coding/neteq4/neteq.gypi @@ -179,6 +179,8 @@ 'tools', ], 'sources': [ + 'tools/audio_loop.cc', + 'tools/audio_loop.h', 'tools/input_audio_file.cc', 'tools/input_audio_file.h', 'tools/rtp_generator.cc', diff --git a/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi b/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi index 96ca1751d..faf7332f2 100644 --- a/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi +++ b/webrtc/modules/audio_coding/neteq4/neteq_tests.gypi @@ -137,6 +137,20 @@ ], }, + { + 'target_name': 'neteq4_speed_test', + 'type': 'executable', + 'dependencies': [ + 'NetEq4', + 'neteq_unittest_tools', + 'PCM16B', + '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', + ], + 'sources': [ + 'test/neteq_speed_test.cc', + ], + }, + { 'target_name': 'NetEq4TestTools', # Collection of useful functions used in other tests. diff --git a/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc b/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc new file mode 100644 index 000000000..d3fcb916b --- /dev/null +++ b/webrtc/modules/audio_coding/neteq4/test/neteq_speed_test.cc @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2013 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 + +#include + +#include "gflags/gflags.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq4/interface/neteq.h" +#include "webrtc/modules/audio_coding/neteq4/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/neteq4/tools/rtp_generator.h" +#include "webrtc/test/testsupport/fileutils.h" +#include "webrtc/typedefs.h" + +using webrtc::NetEq; +using webrtc::test::AudioLoop; +using webrtc::test::RtpGenerator; +using webrtc::WebRtcRTPHeader; + +// Flag validators. +static bool ValidateRuntime(const char* flagname, int value) { + if (value > 0) // Value is ok. + return true; + printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); + return false; +} +static bool ValidateLossrate(const char* flagname, int value) { + if (value >= 0) // Value is ok. + return true; + printf("Invalid value for --%s: %d\n", flagname, static_cast(value)); + return false; +} +static bool ValidateDriftfactor(const char* flagname, double value) { + if (value >= 0.0 && value < 1.0) // Value is ok. + return true; + printf("Invalid value for --%s: %f\n", flagname, value); + return false; +} + +// Define command line flags. +DEFINE_int32(runtime_ms, 10000, "Simulated runtime in ms."); +static const bool runtime_ms_dummy = + google::RegisterFlagValidator(&FLAGS_runtime_ms, &ValidateRuntime); +DEFINE_int32(lossrate, 10, + "Packet lossrate; drop every N packets."); +static const bool lossrate_dummy = + google::RegisterFlagValidator(&FLAGS_lossrate, &ValidateLossrate); +DEFINE_double(drift, 0.1, + "Clockdrift factor."); +static const bool drift_dummy = + google::RegisterFlagValidator(&FLAGS_drift, &ValidateDriftfactor); + +int main(int argc, char* argv[]) { + static const int kMaxChannels = 1; + static const int kMaxSamplesPerMs = 48000 / 1000; + static const int kOutputBlockSizeMs = 10; + const std::string kInputFileName = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + const int kSampRateHz = 32000; + const webrtc::NetEqDecoder kDecoderType = webrtc::kDecoderPCM16Bswb32kHz; + const int kPayloadType = 95; + + std::string program_name = argv[0]; + std::string usage = "Tool for measuring the speed of NetEq.\n" + "Usage: " + program_name + " [options]\n\n" + " --runtime_ms=N runtime in ms; default is 10000 ms\n" + " --lossrate=N drop every N packets; default is 10\n" + " --drift=F clockdrift factor between 0.0 and 1.0; " + "default is 0.1\n"; + google::SetUsageMessage(usage); + google::ParseCommandLineFlags(&argc, &argv, true); + + if (argc != 1) { + // Print usage information. + std::cout << google::ProgramUsage(); + return 0; + } + + // Initialize NetEq instance. + NetEq* neteq = NetEq::Create(kSampRateHz); + // Register decoder in |neteq|. + int error; + error = neteq->RegisterPayloadType(kDecoderType, kPayloadType); + if (error) { + std::cerr << "Cannot register decoder." << std::endl; + exit(1); + } + + // Set up AudioLoop object. + AudioLoop audio_loop; + const size_t kMaxLoopLengthSamples = kSampRateHz * 10; // 10 second loop. + const size_t kInputBlockSizeSamples = 60 * kSampRateHz / 1000; // 60 ms. + if (!audio_loop.Init(kInputFileName, kMaxLoopLengthSamples, + kInputBlockSizeSamples)) { + std::cerr << "Cannot initialize AudioLoop object." << std::endl; + exit(1); + } + + + int32_t time_now_ms = 0; + + // Get first input packet. + WebRtcRTPHeader rtp_header; + RtpGenerator rtp_gen(kSampRateHz / 1000); + // Start with positive drift first half of simulation. + double drift_factor = 0.1; + rtp_gen.set_drift_factor(drift_factor); + bool drift_flipped = false; + int32_t packet_input_time_ms = + rtp_gen.GetRtpHeader(kPayloadType, kInputBlockSizeSamples, &rtp_header); + const int16_t* input_samples = audio_loop.GetNextBlock(); + if (!input_samples) exit(1); + uint8_t input_payload[kInputBlockSizeSamples * sizeof(int16_t)]; + int payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), + kInputBlockSizeSamples, + input_payload); + assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); + + // Main loop. + while (time_now_ms < FLAGS_runtime_ms) { + while (packet_input_time_ms <= time_now_ms) { + // Drop every N packets, where N = FLAGS_lossrate. + bool lost = false; + if (FLAGS_lossrate > 0) { + lost = ((rtp_header.header.sequenceNumber - 1) % FLAGS_lossrate) == 0; + } + if (!lost) { + // Insert packet. + int error = neteq->InsertPacket( + rtp_header, input_payload, payload_len, + packet_input_time_ms * kSampRateHz / 1000); + if (error != NetEq::kOK) { + std::cerr << "InsertPacket returned error code " << + neteq->LastError() << std::endl; + exit(1); + } + } + + // Get next packet. + packet_input_time_ms = rtp_gen.GetRtpHeader(kPayloadType, + kInputBlockSizeSamples, + &rtp_header); + input_samples = audio_loop.GetNextBlock(); + if (!input_samples) exit(1); + payload_len = WebRtcPcm16b_Encode(const_cast(input_samples), + kInputBlockSizeSamples, + input_payload); + assert(payload_len == kInputBlockSizeSamples * sizeof(int16_t)); + } + + // Get output audio, but don't do anything with it. + static const int kOutDataLen = kOutputBlockSizeMs * kMaxSamplesPerMs * + kMaxChannels; + int16_t out_data[kOutDataLen]; + int num_channels; + int samples_per_channel; + int error = neteq->GetAudio(kOutDataLen, out_data, &samples_per_channel, + &num_channels, NULL); + if (error != NetEq::kOK) { + std::cerr << "GetAudio returned error code " << + neteq->LastError() << std::endl; + exit(1); + } + assert(samples_per_channel == kSampRateHz * 10 / 1000); + + time_now_ms += kOutputBlockSizeMs; + if (time_now_ms >= FLAGS_runtime_ms / 2 && !drift_flipped) { + // Apply negative drift second half of simulation. + rtp_gen.set_drift_factor(-drift_factor); + drift_flipped = true; + } + } + + std::cout << "Simulation done" << std::endl; + delete neteq; + return 0; +} diff --git a/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc b/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc new file mode 100644 index 000000000..94ea5bef0 --- /dev/null +++ b/webrtc/modules/audio_coding/neteq4/tools/audio_loop.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 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/neteq4/tools/audio_loop.h" + +#include +#include +#include + +namespace webrtc { +namespace test { + +bool AudioLoop::Init(const std::string file_name, + size_t max_loop_length_samples, + size_t block_length_samples) { + FILE* fp = fopen(file_name.c_str(), "rb"); + if (!fp) return false; + + audio_array_.reset(new int16_t[max_loop_length_samples + + block_length_samples]); + size_t samples_read = fread(audio_array_.get(), sizeof(int16_t), + max_loop_length_samples, fp); + fclose(fp); + + // Block length must be shorter than the loop length. + if (block_length_samples > samples_read) return false; + + // Add an extra block length of samples to the end of the array, starting + // over again from the beginning of the array. This is done to simplify + // the reading process when reading over the end of the loop. + memcpy(&audio_array_[samples_read], audio_array_.get(), + block_length_samples * sizeof(int16_t)); + + loop_length_samples_ = samples_read; + block_length_samples_ = block_length_samples; + return true; +} + +const int16_t* AudioLoop::GetNextBlock() { + // Check that the AudioLoop is initialized. + if (block_length_samples_ == 0) return NULL; + + const int16_t* output_ptr = &audio_array_[next_index_]; + next_index_ = (next_index_ + block_length_samples_) % loop_length_samples_; + return output_ptr; +} + + +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h b/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h new file mode 100644 index 000000000..038ca370e --- /dev/null +++ b/webrtc/modules/audio_coding/neteq4/tools/audio_loop.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_ + +#include + +#include "webrtc/system_wrappers/interface/constructor_magic.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class serving as an infinite source of audio, realized by looping an audio +// clip. +class AudioLoop { + public: + AudioLoop() + : next_index_(0), + loop_length_samples_(0), + block_length_samples_(0), + audio_array_(NULL) { + } + + virtual ~AudioLoop() {} + + // Initializes the AudioLoop by reading from |file_name|. The loop will be no + // longer than |max_loop_length_samples|, if the length of the file is + // greater. Otherwise, the loop length is the same as the file length. + // The audio will be delivered in blocks of |block_length_samples|. + // Returns false if the initialization failed, otherwise true. + bool Init(const std::string file_name, size_t max_loop_length_samples, + size_t block_length_samples); + + // Returns a pointer to the next block of audio. The number given as + // |block_length_samples| to the Init() function determines how many samples + // that can be safely read from the pointer. + const int16_t* GetNextBlock(); + + private: + size_t next_index_; + size_t loop_length_samples_; + size_t block_length_samples_; + scoped_array audio_array_; + + DISALLOW_COPY_AND_ASSIGN(AudioLoop); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ4_TOOLS_AUDIO_LOOP_H_