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 |