diff options
-rw-r--r-- | base/latebindingsymboltable.cc | 2 | ||||
-rw-r--r-- | modules/audio_coding/main/acm2/acm_send_test.cc | 139 | ||||
-rw-r--r-- | modules/audio_coding/main/acm2/acm_send_test.h | 85 | ||||
-rw-r--r-- | modules/audio_coding/main/acm2/audio_coding_module.gypi | 13 | ||||
-rw-r--r-- | modules/audio_coding/main/acm2/audio_coding_module_unittest.cc | 213 | ||||
-rw-r--r-- | modules/modules.gyp | 1 |
6 files changed, 440 insertions, 13 deletions
diff --git a/base/latebindingsymboltable.cc b/base/latebindingsymboltable.cc index 1896bd0f..030f7208 100644 --- a/base/latebindingsymboltable.cc +++ b/base/latebindingsymboltable.cc @@ -100,7 +100,7 @@ bool LateBindingSymbolTable::LoadFromPath(const char *dll_path) { // is necessary for same-named symbols in different ABI // versions of the same library to not explode. RTLD_NOW|RTLD_LOCAL -#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) +#if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID) && defined(RTLD_DEEPBIND) // RTLD_DEEPBIND makes symbol dependencies in the // newly-loaded tree prefer to resolve to definitions within // that tree (the default on OS X). This is necessary for diff --git a/modules/audio_coding/main/acm2/acm_send_test.cc b/modules/audio_coding/main/acm2/acm_send_test.cc new file mode 100644 index 00000000..67cc9b2b --- /dev/null +++ b/modules/audio_coding/main/acm2/acm_send_test.cc @@ -0,0 +1,139 @@ +/* + * 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/main/acm2/acm_send_test.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/checks.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" + +namespace webrtc { +namespace test { + +AcmSendTest::AcmSendTest(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms) + : clock_(0), + acm_(webrtc::AudioCodingModule::Create(0, &clock_)), + audio_source_(audio_source), + source_rate_hz_(source_rate_hz), + input_block_size_samples_(source_rate_hz_ * kBlockSizeMs / 1000), + codec_registered_(false), + test_duration_ms_(test_duration_ms), + frame_type_(kAudioFrameSpeech), + payload_type_(0), + timestamp_(0), + sequence_number_(0) { + input_frame_.sample_rate_hz_ = source_rate_hz_; + input_frame_.num_channels_ = 1; + input_frame_.samples_per_channel_ = input_block_size_samples_; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + acm_->RegisterTransportCallback(this); +} + +bool AcmSendTest::RegisterCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples) { + FATAL_ERROR_IF(AudioCodingModule::Codec( + payload_name, &codec_, sampling_freq_hz, channels) != 0); + codec_.pltype = payload_type; + codec_.pacsize = frame_size_samples; + codec_registered_ = (acm_->RegisterSendCodec(codec_) == 0); + assert(channels == 1); // TODO(henrik.lundin) Add multi-channel support. + input_frame_.num_channels_ = channels; + assert(input_block_size_samples_ * input_frame_.num_channels_ <= + AudioFrame::kMaxDataSizeSamples); + return codec_registered_; +} + +Packet* AcmSendTest::NextPacket() { + assert(codec_registered_); + if (filter_.test(payload_type_)) { + // This payload type should be filtered out. Since the payload type is the + // same throughout the whole test run, no packet at all will be delivered. + // We can just as well signal that the test is over by returning NULL. + return NULL; + } + // Insert audio and process until one packet is produced. + while (clock_.TimeInMilliseconds() < test_duration_ms_) { + clock_.AdvanceTimeMilliseconds(kBlockSizeMs); + FATAL_ERROR_IF( + !audio_source_->Read(input_block_size_samples_, input_frame_.data_)); + FATAL_ERROR_IF(acm_->Add10MsData(input_frame_) != 0); + input_frame_.timestamp_ += input_block_size_samples_; + int32_t encoded_bytes = acm_->Process(); + if (encoded_bytes > 0) { + // Encoded packet received. + return CreatePacket(); + } + } + // Test ended. + return NULL; +} + +// This method receives the callback from ACM when a new packet is produced. +int32_t AcmSendTest::SendData(FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) { + // Store the packet locally. + frame_type_ = frame_type; + payload_type_ = payload_type; + timestamp_ = timestamp; + last_payload_vec_.assign(payload_data, payload_data + payload_len_bytes); + assert(last_payload_vec_.size() == payload_len_bytes); + return 0; +} + +Packet* AcmSendTest::CreatePacket() { + const size_t kRtpHeaderSize = 12; + size_t allocated_bytes = last_payload_vec_.size() + kRtpHeaderSize; + uint8_t* packet_memory = new uint8_t[allocated_bytes]; + // Populate the header bytes. + packet_memory[0] = 0x80; + packet_memory[1] = payload_type_; + packet_memory[2] = (sequence_number_ >> 8) & 0xFF; + packet_memory[3] = (sequence_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; + // Set SSRC to 0x12345678. + packet_memory[8] = 0x12; + packet_memory[9] = 0x34; + packet_memory[10] = 0x56; + packet_memory[11] = 0x78; + + ++sequence_number_; + + // Copy the payload data. + memcpy(packet_memory + kRtpHeaderSize, + &last_payload_vec_[0], + last_payload_vec_.size()); + Packet* packet = + new Packet(packet_memory, allocated_bytes, clock_.TimeInMilliseconds()); + assert(packet); + assert(packet->valid_header()); + return packet; +} + +} // namespace test +} // namespace webrtc diff --git a/modules/audio_coding/main/acm2/acm_send_test.h b/modules/audio_coding/main/acm2/acm_send_test.h new file mode 100644 index 00000000..5e9bd977 --- /dev/null +++ b/modules/audio_coding/main/acm2/acm_send_test.h @@ -0,0 +1,85 @@ +/* + * 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_MAIN_ACM2_ACM_SEND_TEST_H_ +#define WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ + +#include <vector> + +#include "webrtc/base/constructormagic.h" +#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet_source.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +namespace test { +class InputAudioFile; +class Packet; + +class AcmSendTest : public AudioPacketizationCallback, public PacketSource { + public: + AcmSendTest(InputAudioFile* audio_source, + int source_rate_hz, + int test_duration_ms); + virtual ~AcmSendTest() {} + + // Registers the send codec. Returns true on success, false otherwise. + bool RegisterCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples); + + // Returns the next encoded packet. Returns NULL if the test duration was + // exceeded. Ownership of the packet is handed over to the caller. + // Inherited from PacketSource. + Packet* NextPacket(); + + // Inherited from AudioPacketizationCallback. + virtual int32_t SendData( + FrameType frame_type, + uint8_t payload_type, + uint32_t timestamp, + const uint8_t* payload_data, + uint16_t payload_len_bytes, + const RTPFragmentationHeader* fragmentation) OVERRIDE; + + private: + static const int kBlockSizeMs = 10; + + // Creates a Packet object from the last packet produced by ACM (and received + // through the SendData method as a callback). Ownership of the new Packet + // object is transferred to the caller. + Packet* CreatePacket(); + + SimulatedClock clock_; + scoped_ptr<AudioCodingModule> acm_; + InputAudioFile* audio_source_; + int source_rate_hz_; + const int input_block_size_samples_; + AudioFrame input_frame_; + CodecInst codec_; + bool codec_registered_; + int test_duration_ms_; + // The following member variables are set whenever SendData() is called. + FrameType frame_type_; + int payload_type_; + uint32_t timestamp_; + uint16_t sequence_number_; + std::vector<uint8_t> last_payload_vec_; + + DISALLOW_COPY_AND_ASSIGN(AcmSendTest); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_MAIN_ACM2_ACM_SEND_TEST_H_ diff --git a/modules/audio_coding/main/acm2/audio_coding_module.gypi b/modules/audio_coding/main/acm2/audio_coding_module.gypi index dccfe682..f88dbd37 100644 --- a/modules/audio_coding/main/acm2/audio_coding_module.gypi +++ b/modules/audio_coding/main/acm2/audio_coding_module.gypi @@ -130,6 +130,19 @@ ], }, # acm_receive_test { + 'target_name': 'acm_send_test', + 'type': 'static_library', + 'dependencies': [ + 'audio_coding_module', + 'neteq_unittest_tools', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'acm_send_test.cc', + 'acm_send_test.h', + ], + }, # acm_send_test + { 'target_name': 'delay_test', 'type': 'executable', 'dependencies': [ diff --git a/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc b/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc index 7e2ede50..9c21fece 100644 --- a/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc +++ b/modules/audio_coding/main/acm2/audio_coding_module_unittest.cc @@ -12,12 +12,16 @@ #include <vector> #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/md5digest.h" #include "webrtc/modules/audio_coding/main/acm2/acm_receive_test.h" +#include "webrtc/modules/audio_coding/main/acm2/acm_send_test.h" #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #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/input_audio_file.h" #include "webrtc/modules/audio_coding/neteq/tools/output_audio_file.h" +#include "webrtc/modules/audio_coding/neteq/tools/packet.h" #include "webrtc/modules/audio_coding/neteq/tools/rtp_file_source.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/system_wrappers/interface/clock.h" @@ -516,6 +520,19 @@ TEST_F(AcmIsacMtTest, DoTest) { } class AcmReceiverBitExactness : public ::testing::Test { + public: + static std::string PlatformChecksum(std::string win64, + std::string android, + std::string others) { +#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) + return win64; +#elif defined(WEBRTC_ANDROID) + return android; +#else + return others; +#endif + } + protected: void Run(int output_freq_hz, const std::string& checksum_ref) { const std::string input_file_name = @@ -546,18 +563,6 @@ class AcmReceiverBitExactness : public ::testing::Test { std::string checksum_string = checksum.Finish(); EXPECT_EQ(checksum_ref, checksum_string); } - - static std::string PlatformChecksum(std::string win64, - std::string android, - std::string others) { -#if defined(_WIN32) && defined(WEBRTC_ARCH_64_BITS) - return win64; -#elif defined(WEBRTC_ANDROID) - return android; -#else - return others; -#endif - } }; TEST_F(AcmReceiverBitExactness, 8kHzOutput) { @@ -587,4 +592,188 @@ TEST_F(AcmReceiverBitExactness, 48kHzOutput) { "76b9e99e0a3998aa28355e7a2bd836f7", "89b4b19bdb4de40f1d88302ef8cb9f9b")); } + +// This test verifies bit exactness for the send-side of ACM. The test setup is +// a chain of three different test classes: +// +// test::AcmSendTest -> AcmSenderBitExactness -> test::AcmReceiveTest +// +// The receiver side is driving the test by requesting new packets from +// AcmSenderBitExactness::NextPacket(). This method, in turn, asks for the +// packet from test::AcmSendTest::NextPacket, which inserts audio from the +// input file until one packet is produced. (The input file loops indefinitely.) +// Before passing the packet to the receiver, this test class verifies the +// packet header and updates a payload checksum with the new payload. The +// decoded output from the receiver is also verified with a (separate) checksum. +class AcmSenderBitExactness : public ::testing::Test, + public test::PacketSource { + protected: + static const int kTestDurationMs = 1000; + + AcmSenderBitExactness() + : frame_size_rtp_timestamps_(0), + packet_count_(0), + payload_type_(0), + last_sequence_number_(0), + last_timestamp_(0) {} + + // Sets up the test::AcmSendTest object. Returns true on success, otherwise + // false. + bool SetUpSender() { + const std::string input_file_name = + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); + // Note that |audio_source_| will loop forever. The test duration is set + // explicitly by |kTestDurationMs|. + audio_source_.reset(new test::InputAudioFile(input_file_name)); + static const int kSourceRateHz = 32000; + send_test_.reset(new test::AcmSendTest( + audio_source_.get(), kSourceRateHz, kTestDurationMs)); + return send_test_.get() != NULL; + } + + // Registers a send codec in the test::AcmSendTest object. Returns true on + // success, false on failure. + bool RegisterSendCodec(const char* payload_name, + int sampling_freq_hz, + int channels, + int payload_type, + int frame_size_samples, + int frame_size_rtp_timestamps) { + payload_type_ = payload_type; + frame_size_rtp_timestamps_ = frame_size_rtp_timestamps; + return send_test_->RegisterCodec(payload_name, + sampling_freq_hz, + channels, + payload_type, + frame_size_samples); + } + + // Runs the test. SetUpSender() and RegisterSendCodec() must have been called + // before calling this method. + void Run(const std::string& audio_checksum_ref, + const std::string& payload_checksum_ref, + int expected_packets) { + // Set up the receiver used to decode the packets and verify the decoded + // output. + test::AudioChecksum audio_checksum; + 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 checksum calculator. + test::AudioSinkFork output(&audio_checksum, &output_file); + const int kOutputFreqHz = 8000; + test::AcmReceiveTest receive_test(this, &output, kOutputFreqHz); + ASSERT_NO_FATAL_FAILURE(receive_test.RegisterDefaultCodecs()); + + // This is where the actual test is executed. + receive_test.Run(); + + // Extract and verify the audio checksum. + std::string checksum_string = audio_checksum.Finish(); + EXPECT_EQ(audio_checksum_ref, checksum_string); + + // Extract and verify the payload checksum. + char checksum_result[rtc::Md5Digest::kSize]; + payload_checksum_.Finish(checksum_result, rtc::Md5Digest::kSize); + checksum_string = rtc::hex_encode(checksum_result, rtc::Md5Digest::kSize); + EXPECT_EQ(payload_checksum_ref, checksum_string); + + // Verify number of packets produced. + EXPECT_EQ(expected_packets, packet_count_); + } + + // Returns a pointer to the next packet. Returns NULL if the source is + // depleted (i.e., the test duration is exceeded), or if an error occurred. + // Inherited from test::PacketSource. + test::Packet* NextPacket() OVERRIDE { + // Get the next packet from AcmSendTest. Ownership of |packet| is + // transferred to this method. + test::Packet* packet = send_test_->NextPacket(); + if (!packet) + return NULL; + + VerifyPacket(packet); + // TODO(henrik.lundin) Save the packet to file as well. + + // Pass it on to the caller. The caller becomes the owner of |packet|. + return packet; + } + + // Verifies the packet. + void VerifyPacket(const test::Packet* packet) { + EXPECT_TRUE(packet->valid_header()); + // (We can check the header fields even if valid_header() is false.) + EXPECT_EQ(payload_type_, packet->header().payloadType); + if (packet_count_ > 0) { + // This is not the first packet. + uint16_t sequence_number_diff = + packet->header().sequenceNumber - last_sequence_number_; + EXPECT_EQ(1, sequence_number_diff); + uint32_t timestamp_diff = packet->header().timestamp - last_timestamp_; + EXPECT_EQ(frame_size_rtp_timestamps_, timestamp_diff); + } + ++packet_count_; + last_sequence_number_ = packet->header().sequenceNumber; + last_timestamp_ = packet->header().timestamp; + // Update the checksum. + payload_checksum_.Update(packet->payload(), packet->payload_length_bytes()); + } + + void SetUpTest(const char* codec_name, + int codec_sample_rate_hz, + int channels, + int payload_type, + int codec_frame_size_samples, + int codec_frame_size_rtp_timestamps) { + ASSERT_TRUE(SetUpSender()); + ASSERT_TRUE(RegisterSendCodec(codec_name, + codec_sample_rate_hz, + channels, + payload_type, + codec_frame_size_samples, + codec_frame_size_rtp_timestamps)); + } + + scoped_ptr<test::AcmSendTest> send_test_; + scoped_ptr<test::InputAudioFile> audio_source_; + uint32_t frame_size_rtp_timestamps_; + int packet_count_; + uint8_t payload_type_; + uint16_t last_sequence_number_; + uint32_t last_timestamp_; + rtc::Md5Digest payload_checksum_; +}; + +TEST_F(AcmSenderBitExactness, IsacWb30ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 480, 480)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "c7e5bdadfa2871df95639fcc297cf23d", + "0499ca260390769b3172136faad925b9", + "0b58f9eeee43d5891f5f6c75e77984a3"), + AcmReceiverBitExactness::PlatformChecksum( + "d42cb5195463da26c8129bbfe73a22e6", + "83de248aea9c3c2bd680b6952401b4ca", + "3c79f16f34218271f3dca4e2b1dfe1bb"), + 33); +} + +TEST_F(AcmSenderBitExactness, IsacWb60ms) { + ASSERT_NO_FATAL_FAILURE(SetUpTest("ISAC", 16000, 1, 103, 960, 960)); + Run(AcmReceiverBitExactness::PlatformChecksum( + "14d63c5f08127d280e722e3191b73bdd", + "8da003e16c5371af2dc2be79a50f9076", + "1ad29139a04782a33daad8c2b9b35875"), + AcmReceiverBitExactness::PlatformChecksum( + "ebe04a819d3a9d83a83a17f271e1139a", + "97aeef98553b5a4b5a68f8b716e8eaf0", + "9e0a0ab743ad987b55b8e14802769c56"), + 16); +} + } // namespace webrtc diff --git a/modules/modules.gyp b/modules/modules.gyp index e6919f1a..f68ea7cb 100644 --- a/modules/modules.gyp +++ b/modules/modules.gyp @@ -69,6 +69,7 @@ ], 'dependencies': [ 'acm_receive_test', + 'acm_send_test', 'audio_coding_module', 'audio_processing', 'bitrate_controller', |