From 81a78930eeeede0fdaeb0553c6b57acb33f985e7 Mon Sep 17 00:00:00 2001 From: "henrik.lundin@webrtc.org" Date: Tue, 14 Oct 2014 10:49:58 +0000 Subject: [PATCH] New ACM test to trigger audio glitch when switching output sample rate This CL implements a new unit test. The test is designed to trigger a problem in ACM where switching the desired output frequency creates a short discontinuity in the output audio. The problem itself is not solved in this CL, but the failing test is disabled for now. BUG=3919 R=kwiberg@webrtc.org Review URL: https://webrtc-codereview.appspot.com/23019004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7443 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../main/acm2/acm_receive_test_oldapi.cc | 27 +++++ .../main/acm2/acm_receive_test_oldapi.h | 29 ++++- .../audio_coding_module_unittest_oldapi.cc | 102 ++++++++++++++++++ webrtc/modules/audio_coding/neteq/neteq.gypi | 2 + .../neteq/tools/constant_pcm_packet_source.cc | 71 ++++++++++++ .../neteq/tools/constant_pcm_packet_source.h | 57 ++++++++++ 6 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc create mode 100644 webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc index b0c9af138..391e99b03 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc +++ b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc @@ -164,6 +164,7 @@ void AcmReceiveTestOldApi::Run() { } ASSERT_TRUE(audio_sink_->WriteAudioFrame(output_frame)); clock_.AdvanceTimeMilliseconds(10); + AfterGetAudio(); } // Insert packet after converting from RTPHeader to WebRtcRTPHeader. @@ -183,5 +184,31 @@ void AcmReceiveTestOldApi::Run() { } } +AcmReceiveTestToggleOutputFreqOldApi::AcmReceiveTestToggleOutputFreqOldApi( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz_1, + int output_freq_hz_2, + int toggle_period_ms, + NumOutputChannels exptected_output_channels) + : AcmReceiveTestOldApi(packet_source, + audio_sink, + output_freq_hz_1, + exptected_output_channels), + output_freq_hz_1_(output_freq_hz_1), + output_freq_hz_2_(output_freq_hz_2), + toggle_period_ms_(toggle_period_ms), + last_toggle_time_ms_(clock_.TimeInMilliseconds()) { +} + +void AcmReceiveTestToggleOutputFreqOldApi::AfterGetAudio() { + if (clock_.TimeInMilliseconds() >= last_toggle_time_ms_ + toggle_period_ms_) { + output_freq_hz_ = (output_freq_hz_ == output_freq_hz_1_) + ? output_freq_hz_2_ + : output_freq_hz_1_; + last_toggle_time_ms_ = clock_.TimeInMilliseconds(); + } +} + } // namespace test } // namespace webrtc diff --git a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h index 795893c2c..e913fcf3d 100644 --- a/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h +++ b/webrtc/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h @@ -47,17 +47,42 @@ class AcmReceiveTestOldApi { // Runs the test and returns true if successful. void Run(); - private: + protected: + // Method is called after each block of output audio is received from ACM. + virtual void AfterGetAudio() {} + SimulatedClock clock_; scoped_ptr acm_; PacketSource* packet_source_; AudioSink* audio_sink_; - const int output_freq_hz_; + int output_freq_hz_; NumOutputChannels exptected_output_channels_; DISALLOW_COPY_AND_ASSIGN(AcmReceiveTestOldApi); }; +// This test toggles the output frequency every |toggle_period_ms|. The test +// starts with |output_freq_hz_1|. Except for the toggling, it does the same +// thing as AcmReceiveTestOldApi. +class AcmReceiveTestToggleOutputFreqOldApi : public AcmReceiveTestOldApi { + public: + AcmReceiveTestToggleOutputFreqOldApi( + PacketSource* packet_source, + AudioSink* audio_sink, + int output_freq_hz_1, + int output_freq_hz_2, + int toggle_period_ms, + NumOutputChannels exptected_output_channels); + + protected: + void AfterGetAudio() OVERRIDE; + + const int output_freq_hz_1_; + const int output_freq_hz_2_; + const int toggle_period_ms_; + int64_t last_toggle_time_ms_; +}; + } // namespace test } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_RECEIVE_TEST_H_ diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc index 0e9d27364..33c364a16 100644 --- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc +++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc @@ -20,6 +20,7 @@ #include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h" #include "webrtc/modules/audio_coding/neteq/tools/audio_checksum.h" #include "webrtc/modules/audio_coding/neteq/tools/audio_loop.h" +#include "webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h" #include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" #include "webrtc/modules/audio_coding/neteq/tools/packet.h" @@ -935,4 +936,105 @@ TEST_F(AcmSenderBitExactnessOldApi, Opus_stereo_20ms) { test::AcmReceiveTestOldApi::kStereoOutput); } +// This test fixture is implemented to run ACM and change the desired output +// frequency during the call. The input packets are simply PCM16b-wb encoded +// payloads with a constant value of |kSampleValue|. The test fixture itself +// acts as PacketSource in between the receive test class and the constant- +// payload packet source class. The output is both written to file, and analyzed +// in this test fixture. +class AcmSwitchingOutputFrequencyOldApi : public ::testing::Test, + public test::PacketSource, + public test::AudioSink { + protected: + static const size_t kTestNumPackets = 100; + static const int kEncodedSampleRateHz = 16000; + static const size_t kPayloadLenSamples = 30 * kEncodedSampleRateHz / 1000; + static const int kPayloadType = 108; // Default payload type for PCM16b-wb. + + AcmSwitchingOutputFrequencyOldApi() + : first_output_(true), + num_packets_(0), + packet_source_(kPayloadLenSamples, + kSampleValue, + kEncodedSampleRateHz, + kPayloadType), + high_output_freq_(0), + has_toggled_(false) {} + + void Run(int low_output_freq, int high_output_freq, int toggle_period_ms) { + // Set up the receiver used to decode the packets and verify the decoded + // output. + const std::string output_file_name = + webrtc::test::OutputPath() + + ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_case_name() + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + "_output.pcm"; + test::OutputAudioFile output_file(output_file_name); + // Have the output audio sent both to file and to the WriteArray method in + // this class. + test::AudioSinkFork output(this, &output_file); + test::AcmReceiveTestToggleOutputFreqOldApi receive_test( + this, + &output, + low_output_freq, + high_output_freq, + toggle_period_ms, + test::AcmReceiveTestOldApi::kMonoOutput); + ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); + high_output_freq_ = high_output_freq; + + // This is where the actual test is executed. + receive_test.Run(); + } + + // Inherited from test::PacketSource. + test::Packet* NextPacket() OVERRIDE { + // Check if it is time to terminate the test. The packet source is of type + // ConstantPcmPacketSource, which is infinite, so we must end the test + // "manually". + if (num_packets_++ > kTestNumPackets) { + EXPECT_TRUE(has_toggled_); + return NULL; // Test ended. + } + + // Get the next packet from the source. + return packet_source_.NextPacket(); + } + + // Inherited from test::AudioSink. + bool WriteArray(const int16_t* audio, size_t num_samples) { + // Skip checking the first output frame, since it has a number of zeros + // due to how NetEq is initialized. + if (first_output_) { + first_output_ = false; + return true; + } + for (size_t i = 0; i < num_samples; ++i) { + EXPECT_EQ(kSampleValue, audio[i]); + } + if (num_samples == + static_cast(high_output_freq_ / 100)) // Size of 10 ms frame. + has_toggled_ = true; + // The return value does not say if the values match the expectation, just + // that the method could process the samples. + return true; + } + + const int16_t kSampleValue = 1000; + bool first_output_; + size_t num_packets_; + test::ConstantPcmPacketSource packet_source_; + int high_output_freq_; + bool has_toggled_; +}; + +TEST_F(AcmSwitchingOutputFrequencyOldApi, TestWithoutToggling) { + Run(16000, 16000, 1000); +} + +TEST_F(AcmSwitchingOutputFrequencyOldApi, DISABLED_TestWithToggling) { + Run(16000, 32000, 1000); +} } // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq/neteq.gypi b/webrtc/modules/audio_coding/neteq/neteq.gypi index 34d64a1fb..0901615af 100644 --- a/webrtc/modules/audio_coding/neteq/neteq.gypi +++ b/webrtc/modules/audio_coding/neteq/neteq.gypi @@ -185,6 +185,8 @@ 'tools/audio_loop.cc', 'tools/audio_loop.h', 'tools/audio_sink.h', + 'tools/constant_pcm_packet_source.cc', + 'tools/constant_pcm_packet_source.h', 'tools/input_audio_file.cc', 'tools/input_audio_file.h', 'tools/output_audio_file.h', diff --git a/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc b/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc new file mode 100644 index 000000000..a92f41255 --- /dev/null +++ b/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc @@ -0,0 +1,71 @@ +/* + * 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/neteq/tools/constant_pcm_packet_source.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +namespace webrtc { +namespace test { + +ConstantPcmPacketSource::ConstantPcmPacketSource(size_t payload_len_samples, + int16_t sample_value, + int sample_rate_hz, + int payload_type) + : payload_len_samples_(payload_len_samples), + packet_len_bytes_(2 * payload_len_samples_ + kHeaderLenBytes), + samples_per_ms_(sample_rate_hz / 1000), + next_arrival_time_ms_(0.0), + payload_type_(payload_type), + seq_number_(0), + timestamp_(0), + payload_ssrc_(0xABCD1234) { + int encoded_len = WebRtcPcm16b_EncodeW16(&sample_value, 1, &encoded_sample_); + CHECK_EQ(encoded_len, 2); +} + +Packet* ConstantPcmPacketSource::NextPacket() { + CHECK_GT(packet_len_bytes_, kHeaderLenBytes); + uint8_t* packet_memory = new uint8_t[packet_len_bytes_]; + // Fill the payload part of the packet memory with the pre-encoded value. + std::fill_n(reinterpret_cast(packet_memory + kHeaderLenBytes), + payload_len_samples_, + encoded_sample_); + WriteHeader(packet_memory); + // |packet| assumes ownership of |packet_memory|. + Packet* packet = + new Packet(packet_memory, packet_len_bytes_, next_arrival_time_ms_); + next_arrival_time_ms_ += payload_len_samples_ / samples_per_ms_; + return packet; +} + +void ConstantPcmPacketSource::WriteHeader(uint8_t* packet_memory) { + packet_memory[0] = 0x80; + packet_memory[1] = payload_type_ & 0xFF; + packet_memory[2] = (seq_number_ >> 8) & 0xFF; + packet_memory[3] = seq_number_ & 0xFF; + packet_memory[4] = (timestamp_ >> 24) & 0xFF; + packet_memory[5] = (timestamp_ >> 16) & 0xFF; + packet_memory[6] = (timestamp_ >> 8) & 0xFF; + packet_memory[7] = timestamp_ & 0xFF; + packet_memory[8] = (payload_ssrc_ >> 24) & 0xFF; + packet_memory[9] = (payload_ssrc_ >> 16) & 0xFF; + packet_memory[10] = (payload_ssrc_ >> 8) & 0xFF; + packet_memory[11] = payload_ssrc_ & 0xFF; + ++seq_number_; + timestamp_ += static_cast(payload_len_samples_); +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h b/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h new file mode 100644 index 000000000..8a4bbf172 --- /dev/null +++ b/webrtc/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_ + +#include +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" + +namespace webrtc { +namespace test { + +// This class implements a packet source that delivers PCM16b encoded packets +// with a constant sample value. The payload length, constant sample value, +// sample rate, and payload type are all set in the constructor. +class ConstantPcmPacketSource : public PacketSource { + public: + ConstantPcmPacketSource(size_t payload_len_samples, + int16_t sample_value, + int sample_rate_hz, + int payload_type); + + // Returns a pointer to the next packet. Will never return NULL. That is, + // the source is infinite. + Packet* NextPacket() OVERRIDE; + + private: + void WriteHeader(uint8_t* packet_memory); + + const size_t kHeaderLenBytes = 12; + const size_t payload_len_samples_; + const size_t packet_len_bytes_; + int16_t encoded_sample_; + const int samples_per_ms_; + double next_arrival_time_ms_; + const int payload_type_; + uint16_t seq_number_; + uint32_t timestamp_; + const uint32_t payload_ssrc_; + + DISALLOW_COPY_AND_ASSIGN(ConstantPcmPacketSource); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_CONSTANT_PCM_PACKET_SOURCE_H_