diff options
author | henrik.lundin@webrtc.org <henrik.lundin@webrtc.org> | 2014-10-14 10:49:58 +0000 |
---|---|---|
committer | henrik.lundin@webrtc.org <henrik.lundin@webrtc.org> | 2014-10-14 10:49:58 +0000 |
commit | 4dd7210b1a0ab6e85a16cf5abb0b439d300b7744 (patch) | |
tree | d53bfd79eb8fb6c401db171c78ee6f6a8b07ab4e | |
parent | 6368255205b7849b9ead613754cb5c31c8b0294b (diff) | |
download | webrtc-4dd7210b1a0ab6e85a16cf5abb0b439d300b7744.tar.gz |
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/webrtc@7443 4adac7df-926f-26a2-2b94-8c16560cd09d
6 files changed, 286 insertions, 2 deletions
diff --git a/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc b/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc index b0c9af13..391e99b0 100644 --- a/modules/audio_coding/main/acm2/acm_receive_test_oldapi.cc +++ b/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/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h b/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h index 795893c2..e913fcf3 100644 --- a/modules/audio_coding/main/acm2/acm_receive_test_oldapi.h +++ b/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<AudioCodingModule> 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/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc b/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc index 0e9d2736..33c364a1 100644 --- a/modules/audio_coding/main/acm2/audio_coding_module_unittest_oldapi.cc +++ b/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<size_t>(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/modules/audio_coding/neteq/neteq.gypi b/modules/audio_coding/neteq/neteq.gypi index 34d64a1f..0901615a 100644 --- a/modules/audio_coding/neteq/neteq.gypi +++ b/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/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc b/modules/audio_coding/neteq/tools/constant_pcm_packet_source.cc new file mode 100644 index 00000000..a92f4125 --- /dev/null +++ b/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 <algorithm> + +#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<int16_t*>(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<uint32_t>(payload_len_samples_); +} + +} // namespace test +} // namespace webrtc diff --git a/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h b/modules/audio_coding/neteq/tools/constant_pcm_packet_source.h new file mode 100644 index 00000000..8a4bbf17 --- /dev/null +++ b/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 <stdio.h> +#include <string> + +#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_ |