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
This commit is contained in:
henrik.lundin@webrtc.org 2014-10-14 10:49:58 +00:00
parent c216b9aeaf
commit 81a78930ee
6 changed files with 286 additions and 2 deletions

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -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_