Chromium Code Reviews| Index: webrtc/modules/audio_coding/codecs/isac/unittest.cc |
| diff --git a/webrtc/modules/audio_coding/codecs/isac/unittest.cc b/webrtc/modules/audio_coding/codecs/isac/unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..842c67c56468e96e1c6a4eb69b8fa81619269049 |
| --- /dev/null |
| +++ b/webrtc/modules/audio_coding/codecs/isac/unittest.cc |
| @@ -0,0 +1,188 @@ |
| +/* |
| + * Copyright (c) 2015 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 <cmath> |
| +#include <sstream> |
| +#include <vector> |
| + |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "webrtc/base/buffer.h" |
| +#include "webrtc/base/checks.h" |
| +#include "webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h" |
| +#include "webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h" |
| +#include "webrtc/test/testsupport/fileutils.h" |
| + |
| +namespace webrtc { |
| + |
| +namespace { |
| + |
| +std::vector<int16_t> LoadSpeechData() { |
|
hlundin-webrtc
2015/06/26 10:35:15
Consider using webrtc::test::InputAudioFile instea
kwiberg-webrtc
2015/06/28 03:17:41
Done.
|
| + static const int kIsacNumberOfSamples = 32 * 60; // 60 ms at 32 kHz |
| + const std::string file_name = |
| + webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"); |
| + FILE* input_file = fopen(file_name.c_str(), "rb"); |
| + CHECK(input_file); |
| + std::vector<int16_t> speech_data(kIsacNumberOfSamples); |
| + CHECK_EQ(kIsacNumberOfSamples, |
| + static_cast<int32_t>(fread(speech_data.data(), sizeof(int16_t), |
| + kIsacNumberOfSamples, input_file))); |
| + CHECK_EQ(0, fclose(input_file)); |
| +#ifndef WEBRTC_ARCH_LITTLE_ENDIAN |
| +#error "Need to convert samples from little-endian" |
| +#endif |
| + return speech_data; |
| +} |
| + |
| +template <typename T> |
| +IsacBandwidthInfo GetBwInfo(typename T::instance_type* inst) { |
| + IsacBandwidthInfo bi; |
| + T::GetBandwidthInfo(inst, &bi); |
| + CHECK(bi.in_use); |
| + return bi; |
| +} |
| + |
| +std::string Str(const IsacBandwidthInfo& bi) { |
|
hlundin-webrtc
2015/06/26 10:35:14
The function name is a bit too compact. Consider T
kwiberg-webrtc
2015/06/28 03:17:41
Done.
|
| + CHECK(bi.in_use); |
| + std::ostringstream o; |
| + o << "(send_bw_avg: " << bi.send_bw_avg << ", " |
| + << "send_max_delay_avg: " << bi.send_max_delay_avg << ", " |
| + << "bottleneck_idx: " << bi.bottleneck_idx << ", " |
| + << "jitter_info: " << bi.jitter_info << ")"; |
| + return o.str(); |
| +} |
| + |
| +// Is the distance between a and b dist or less, and the relative distance |
| +// rdist or less? |
| +bool AlmostEqual(int a, int b, unsigned dist, double rdist) { |
| + // d = |a-b|, because casting n-bit signed to n-bit unsigned leaves a value |
| + // unchanged mod 2^n. |
| + unsigned d = a > b ? static_cast<unsigned>(a) - static_cast<unsigned>(b) |
| + : static_cast<unsigned>(b) - static_cast<unsigned>(a); |
| + return d <= dist && d <= rdist * std::min(std::abs(a), std::abs(b)); |
| +} |
| + |
| +// Returns true if the two arguments are similar enough. |
| +bool Similar(const IsacBandwidthInfo& a, const IsacBandwidthInfo& b) { |
| + return AlmostEqual(a.send_bw_avg, b.send_bw_avg, 1000, 0.07) && |
| + a.send_max_delay_avg == b.send_max_delay_avg && |
| + AlmostEqual(a.bottleneck_idx, b.bottleneck_idx, 1, 0.1) && |
| + a.jitter_info == b.jitter_info; |
| +} |
| + |
| +// Count the number of elements in the range [lower,upper). |
| +template <typename T> |
| +int CountRange(std::multiset<T> s, T lower, T upper) { |
| + return std::distance(s.lower_bound(lower), s.lower_bound(upper)); |
| +} |
| + |
| +template <typename T> |
| +rtc::Buffer Encode60Ms(typename T::instance_type* inst, |
| + const IsacBandwidthInfo* bi, |
| + const int16_t* speech_data) { |
| + rtc::Buffer output(1000); |
| + for (int i = 0;; ++i) { |
| + if (bi) |
| + T::SetBandwidthInfo(inst, bi); |
| + int encoded_bytes = T::Encode(inst, speech_data, output.data()); |
|
hlundin-webrtc
2015/06/26 10:35:15
What if encoded_bytes is too large for output?
kwiberg-webrtc
2015/06/28 03:17:41
It shouldn't be. And some sanitizer bot would comp
|
| + if (i == 5) { |
| + output.SetSize(encoded_bytes); |
| + return output; |
| + } |
| + CHECK_EQ(0, encoded_bytes); |
|
hlundin-webrtc
2015/06/26 10:35:14
CHECK is quite aggressive here. Consider using EXP
kwiberg-webrtc
2015/06/28 03:17:41
OK. Although I sort of doubt the rest of the run w
hlundin-webrtc
2015/06/29 08:42:18
Right. But a CHECK will kill the test binary, in t
kwiberg-webrtc
2015/06/29 11:53:48
Mmmm, I didn't think things through enough to real
|
| + } |
| +} |
| + |
| +template <typename T> |
| +void TestGetSetBandwidthInfo(const int16_t* speech_data) { |
| + // Conjoined encoder/decoder pair: |
| + typename T::instance_type* encdec; |
| + EXPECT_EQ(0, T::Create(&encdec)); |
|
hlundin-webrtc
2015/06/26 10:35:15
All the create and init calls should be ASSERT_EQ
kwiberg-webrtc
2015/06/28 03:17:41
Done.
|
| + EXPECT_EQ(0, T::EncoderInit(encdec, 0 /* adaptive mode */)); |
| + EXPECT_EQ(0, T::DecoderInit(encdec)); |
| + |
| + // Disjoint encoder/decoder pair: |
| + typename T::instance_type* enc; |
| + EXPECT_EQ(0, T::Create(&enc)); |
| + EXPECT_EQ(0, T::EncoderInit(enc, 0 /* adaptive mode */)); |
| + typename T::instance_type* dec; |
| + EXPECT_EQ(0, T::Create(&dec)); |
| + EXPECT_EQ(0, T::DecoderInit(dec)); |
| + |
| + const int kSamplesPerPacket = 960; |
| + std::multiset<int> send_bw_avg, bottleneck_idx; |
| + for (int i = 0; i < 250; ++i) { |
| + std::ostringstream ss; |
| + ss << "i = " << i; |
| + SCOPED_TRACE(ss.str()); |
| + |
| + // 1. Get BW info from decoders, and make sure they're similar enough. |
| + auto bi1 = GetBwInfo<T>(encdec); |
| + auto bi2 = GetBwInfo<T>(dec); |
| + EXPECT_TRUE(Similar(bi1, bi2)) << "Not similar enough:\n " << Str(bi1) |
| + << "\n " << Str(bi2); |
| + send_bw_avg.insert(bi2.send_bw_avg); |
| + bottleneck_idx.insert(bi2.bottleneck_idx); |
| + |
| + // 2. Encode 6 * 10 ms. The separate encoder is given the BW info before |
| + // each encode call. |
| + auto bitstream1 = Encode60Ms<T>(encdec, nullptr, speech_data); |
| + auto bitstream2 = Encode60Ms<T>(enc, &bi2, speech_data); |
| + |
| + // 3. Deliver the encoded data to the decoders (but don't actually ask them |
| + // to decode it; that's not necessary). |
| + const int send_time = i * kSamplesPerPacket; |
| + const int receive_time = send_time + kSamplesPerPacket / 10; // 6 ms delay |
| + EXPECT_EQ( |
| + 0, T::UpdateBwEstimate(encdec, bitstream1.data(), bitstream1.size(), 1, |
| + send_time, receive_time)); |
| + EXPECT_EQ(0, T::UpdateBwEstimate(dec, bitstream2.data(), bitstream2.size(), |
| + 1, send_time, receive_time)); |
|
kwiberg-webrtc
2015/06/25 14:52:43
If I've understood correctly, this means that each
hlundin-webrtc
2015/06/26 10:35:15
I'm not so sure. The receive time should just be t
kwiberg-webrtc
2015/06/27 23:43:43
But I do get some action. First it keeps the bitra
hlundin-webrtc
2015/06/29 08:42:18
Hmm. I still think that the BWE should ignore this
|
| + } |
| + |
| + // Check that we see a good spread of send_bw_avg numbers. |
| + EXPECT_EQ(CountRange(send_bw_avg, std::numeric_limits<int>::min(), 10000), 0); |
|
hlundin-webrtc
2015/06/26 10:35:15
EXPECT_EQ should have swapped parameters; expected
kwiberg-webrtc
2015/06/28 03:17:41
Done. For symmetry, I also swapped the EXPECT_GEs.
|
| + EXPECT_GE(CountRange(send_bw_avg, 10000, 11000), 37); |
| + EXPECT_GE(CountRange(send_bw_avg, 11000, 12000), 14); |
| + EXPECT_GE(CountRange(send_bw_avg, 12000, 13000), 14); |
| + EXPECT_GE(CountRange(send_bw_avg, 13000, 14000), 10); |
| + EXPECT_GE(CountRange(send_bw_avg, 14000, 15000), 15); |
| + EXPECT_GE(CountRange(send_bw_avg, 15000, 16000), 12); |
| + EXPECT_GE(CountRange(send_bw_avg, 16000, 17000), 10); |
| + EXPECT_GE(CountRange(send_bw_avg, 17000, 18000), 12); |
| + EXPECT_GE(CountRange(send_bw_avg, 18000, 19000), 7); |
| + EXPECT_GE(CountRange(send_bw_avg, 19000, 20000), 40); |
| + EXPECT_GE(CountRange(send_bw_avg, 20000, 21000), 34); |
| + EXPECT_EQ(CountRange(send_bw_avg, 21000, std::numeric_limits<int>::max()), 0); |
| + |
| + // Check that we see a good spread of bottleneck_idx numbers. |
| + EXPECT_EQ(bottleneck_idx.count(7), 1u); |
| + EXPECT_EQ(CountRange(bottleneck_idx, std::numeric_limits<int>::min(), 12), 1); |
| + EXPECT_GE(bottleneck_idx.count(12), 37u); |
| + EXPECT_GE(bottleneck_idx.count(13), 18u); |
| + EXPECT_GE(bottleneck_idx.count(14), 18u); |
| + EXPECT_GE(bottleneck_idx.count(15), 18u); |
| + EXPECT_GE(bottleneck_idx.count(16), 20u); |
| + EXPECT_GE(bottleneck_idx.count(17), 19u); |
| + EXPECT_GE(bottleneck_idx.count(18), 40u); |
| + EXPECT_GE(bottleneck_idx.count(19), 32u); |
| + EXPECT_EQ(CountRange(bottleneck_idx, 20, std::numeric_limits<int>::max()), 0); |
| +} |
| + |
| +} // namespace |
| + |
| +TEST(IsacCommonTest, GetSetBandwidthInfoFloat) { |
| + TestGetSetBandwidthInfo<IsacFloat>(LoadSpeechData().data()); |
| +} |
| + |
| +TEST(IsacCommonTest, GetSetBandwidthInfoFix) { |
| + TestGetSetBandwidthInfo<IsacFix>(LoadSpeechData().data()); |
| +} |
| + |
| +} // namespace webrtc |