1 /*
2  *  Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <array>
12 #include <memory>
13 #include <vector>
14 
15 #include "absl/strings/string_view.h"
16 #include "api/array_view.h"
17 #include "api/audio_codecs/isac/audio_decoder_isac_fix.h"
18 #include "api/audio_codecs/isac/audio_decoder_isac_float.h"
19 #include "api/audio_codecs/isac/audio_encoder_isac_fix.h"
20 #include "api/audio_codecs/isac/audio_encoder_isac_float.h"
21 #include "modules/audio_coding/test/PCMFile.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/strings/string_builder.h"
24 #include "test/gtest.h"
25 #include "test/testsupport/file_utils.h"
26 
27 namespace webrtc {
28 namespace {
29 
30 constexpr int kPayloadType = 42;
31 
32 enum class IsacImpl { kFixed, kFloat };
33 
IsacImplToString(IsacImpl impl)34 absl::string_view IsacImplToString(IsacImpl impl) {
35   switch (impl) {
36     case IsacImpl::kFixed:
37       return "fixed";
38     case IsacImpl::kFloat:
39       return "float";
40   }
41 }
42 
GetPcmTestFileReader(int sample_rate_hz)43 std::unique_ptr<PCMFile> GetPcmTestFileReader(int sample_rate_hz) {
44   std::string filename;
45   switch (sample_rate_hz) {
46     case 16000:
47       filename = test::ResourcePath("audio_coding/testfile16kHz", "pcm");
48       break;
49     case 32000:
50       filename = test::ResourcePath("audio_coding/testfile32kHz", "pcm");
51       break;
52     default:
53       RTC_NOTREACHED() << "No test file available for " << sample_rate_hz
54                        << " Hz.";
55   }
56   auto pcm_file = std::make_unique<PCMFile>();
57   pcm_file->ReadStereo(false);
58   pcm_file->Open(filename, sample_rate_hz, "rb", /*auto_rewind=*/true);
59   pcm_file->FastForward(/*num_10ms_blocks=*/100);  // Skip initial silence.
60   RTC_CHECK(!pcm_file->EndOfFile());
61   return pcm_file;
62 }
63 
64 // Returns a view to the interleaved samples of an AudioFrame object.
AudioFrameToView(const AudioFrame & audio_frame)65 rtc::ArrayView<const int16_t> AudioFrameToView(const AudioFrame& audio_frame) {
66   return {audio_frame.data(),
67           audio_frame.samples_per_channel() * audio_frame.num_channels()};
68 }
69 
CreateEncoder(IsacImpl impl,int sample_rate_hz,int frame_size_ms,int bitrate_bps)70 std::unique_ptr<AudioEncoder> CreateEncoder(IsacImpl impl,
71                                             int sample_rate_hz,
72                                             int frame_size_ms,
73                                             int bitrate_bps) {
74   RTC_CHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000);
75   RTC_CHECK(frame_size_ms == 30 || frame_size_ms == 60);
76   RTC_CHECK_GT(bitrate_bps, 0);
77   switch (impl) {
78     case IsacImpl::kFixed: {
79       AudioEncoderIsacFix::Config config;
80       config.bit_rate = bitrate_bps;
81       config.frame_size_ms = frame_size_ms;
82       RTC_CHECK_EQ(16000, sample_rate_hz);
83       return AudioEncoderIsacFix::MakeAudioEncoder(config, kPayloadType);
84     }
85     case IsacImpl::kFloat: {
86       AudioEncoderIsacFloat::Config config;
87       config.bit_rate = bitrate_bps;
88       config.frame_size_ms = frame_size_ms;
89       config.sample_rate_hz = sample_rate_hz;
90       return AudioEncoderIsacFloat::MakeAudioEncoder(config, kPayloadType);
91     }
92   }
93 }
94 
CreateDecoder(IsacImpl impl,int sample_rate_hz)95 std::unique_ptr<AudioDecoder> CreateDecoder(IsacImpl impl, int sample_rate_hz) {
96   RTC_CHECK(sample_rate_hz == 16000 || sample_rate_hz == 32000);
97   switch (impl) {
98     case IsacImpl::kFixed: {
99       webrtc::AudioDecoderIsacFix::Config config;
100       RTC_CHECK_EQ(16000, sample_rate_hz);
101       return webrtc::AudioDecoderIsacFix::MakeAudioDecoder(config);
102     }
103     case IsacImpl::kFloat: {
104       webrtc::AudioDecoderIsacFloat::Config config;
105       config.sample_rate_hz = sample_rate_hz;
106       return webrtc::AudioDecoderIsacFloat::MakeAudioDecoder(config);
107     }
108   }
109 }
110 
111 struct EncoderTestParams {
112   IsacImpl impl;
113   int sample_rate_hz;
114   int frame_size_ms;
115 };
116 
117 class EncoderTest : public testing::TestWithParam<EncoderTestParams> {
118  protected:
119   EncoderTest() = default;
GetIsacImpl() const120   IsacImpl GetIsacImpl() const { return GetParam().impl; }
GetSampleRateHz() const121   int GetSampleRateHz() const { return GetParam().sample_rate_hz; }
GetFrameSizeMs() const122   int GetFrameSizeMs() const { return GetParam().frame_size_ms; }
123 };
124 
TEST_P(EncoderTest,TestConfig)125 TEST_P(EncoderTest, TestConfig) {
126   for (int bitrate_bps : {10000, 21000, 32000}) {
127     SCOPED_TRACE(bitrate_bps);
128     auto encoder = CreateEncoder(GetIsacImpl(), GetSampleRateHz(),
129                                  GetFrameSizeMs(), bitrate_bps);
130     EXPECT_EQ(GetSampleRateHz(), encoder->SampleRateHz());
131     EXPECT_EQ(size_t{1}, encoder->NumChannels());
132     EXPECT_EQ(bitrate_bps, encoder->GetTargetBitrate());
133   }
134 }
135 
136 // Encodes an input audio sequence with a low and a high target bitrate and
137 // checks that the number of produces bytes in the first case is less than that
138 // of the second case.
TEST_P(EncoderTest,TestDifferentBitrates)139 TEST_P(EncoderTest, TestDifferentBitrates) {
140   auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
141   constexpr int kLowBps = 20000;
142   constexpr int kHighBps = 25000;
143   auto encoder_low = CreateEncoder(GetIsacImpl(), GetSampleRateHz(),
144                                    GetFrameSizeMs(), kLowBps);
145   auto encoder_high = CreateEncoder(GetIsacImpl(), GetSampleRateHz(),
146                                     GetFrameSizeMs(), kHighBps);
147   int num_bytes_low = 0;
148   int num_bytes_high = 0;
149   constexpr int kNumFrames = 12;
150   for (int i = 0; i < kNumFrames; ++i) {
151     AudioFrame in;
152     pcm_file->Read10MsData(in);
153     rtc::Buffer low, high;
154     encoder_low->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &low);
155     encoder_high->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &high);
156     num_bytes_low += low.size();
157     num_bytes_high += high.size();
158   }
159   EXPECT_LT(num_bytes_low, num_bytes_high);
160 }
161 
162 // Checks that, given a target bitrate, the encoder does not overshoot too much.
TEST_P(EncoderTest,DoNotOvershootTargetBitrate)163 TEST_P(EncoderTest, DoNotOvershootTargetBitrate) {
164   for (int bitrate_bps : {10000, 15000, 20000, 26000, 32000}) {
165     SCOPED_TRACE(bitrate_bps);
166     auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
167     auto e = CreateEncoder(GetIsacImpl(), GetSampleRateHz(), GetFrameSizeMs(),
168                            bitrate_bps);
169     int num_bytes = 0;
170     constexpr int kNumFrames = 200;  // 2 seconds.
171     for (int i = 0; i < kNumFrames; ++i) {
172       AudioFrame in;
173       pcm_file->Read10MsData(in);
174       rtc::Buffer encoded;
175       e->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &encoded);
176       num_bytes += encoded.size();
177     }
178     // Inverse of the duration of |kNumFrames| 10 ms frames (unit: seconds^-1).
179     constexpr float kAudioDurationInv = 100.f / kNumFrames;
180     const int measured_bitrate_bps = 8 * num_bytes * kAudioDurationInv;
181     EXPECT_LT(measured_bitrate_bps, bitrate_bps + 2000);  // Max 2 kbps extra.
182   }
183 }
184 
185 // Creates tests for different encoder configurations and implementations.
186 INSTANTIATE_TEST_SUITE_P(
187     IsacApiTest,
188     EncoderTest,
__anon0525dd8c0202null189     ::testing::ValuesIn([] {
190       std::vector<EncoderTestParams> cases;
191       for (IsacImpl impl : {IsacImpl::kFloat, IsacImpl::kFixed}) {
192         for (int frame_size_ms : {30, 60}) {
193           cases.push_back({impl, 16000, frame_size_ms});
194         }
195       }
196       cases.push_back({IsacImpl::kFloat, 32000, 30});
197       return cases;
198     }()),
__anon0525dd8c0302(const ::testing::TestParamInfo<EncoderTestParams>& info) 199     [](const ::testing::TestParamInfo<EncoderTestParams>& info) {
200       rtc::StringBuilder b;
201       const auto& p = info.param;
202       b << IsacImplToString(p.impl) << "_" << p.sample_rate_hz << "_"
203         << p.frame_size_ms;
204       return b.Release();
205     });
206 
207 struct DecoderTestParams {
208   IsacImpl impl;
209   int sample_rate_hz;
210 };
211 
212 class DecoderTest : public testing::TestWithParam<DecoderTestParams> {
213  protected:
214   DecoderTest() = default;
GetIsacImpl() const215   IsacImpl GetIsacImpl() const { return GetParam().impl; }
GetSampleRateHz() const216   int GetSampleRateHz() const { return GetParam().sample_rate_hz; }
217 };
218 
TEST_P(DecoderTest,TestConfig)219 TEST_P(DecoderTest, TestConfig) {
220   auto decoder = CreateDecoder(GetIsacImpl(), GetSampleRateHz());
221   EXPECT_EQ(GetSampleRateHz(), decoder->SampleRateHz());
222   EXPECT_EQ(size_t{1}, decoder->Channels());
223 }
224 
225 // Creates tests for different decoder configurations and implementations.
226 INSTANTIATE_TEST_SUITE_P(
227     IsacApiTest,
228     DecoderTest,
229     ::testing::ValuesIn({DecoderTestParams{IsacImpl::kFixed, 16000},
230                          DecoderTestParams{IsacImpl::kFloat, 16000},
231                          DecoderTestParams{IsacImpl::kFloat, 32000}}),
__anon0525dd8c0402(const ::testing::TestParamInfo<DecoderTestParams>& info) 232     [](const ::testing::TestParamInfo<DecoderTestParams>& info) {
233       const auto& p = info.param;
234       return (rtc::StringBuilder()
235               << IsacImplToString(p.impl) << "_" << p.sample_rate_hz)
236           .Release();
237     });
238 
239 struct EncoderDecoderPairTestParams {
240   int sample_rate_hz;
241   int frame_size_ms;
242   IsacImpl encoder_impl;
243   IsacImpl decoder_impl;
244 };
245 
246 class EncoderDecoderPairTest
247     : public testing::TestWithParam<EncoderDecoderPairTestParams> {
248  protected:
249   EncoderDecoderPairTest() = default;
GetSampleRateHz() const250   int GetSampleRateHz() const { return GetParam().sample_rate_hz; }
GetEncoderFrameSizeMs() const251   int GetEncoderFrameSizeMs() const { return GetParam().frame_size_ms; }
GetEncoderIsacImpl() const252   IsacImpl GetEncoderIsacImpl() const { return GetParam().encoder_impl; }
GetDecoderIsacImpl() const253   IsacImpl GetDecoderIsacImpl() const { return GetParam().decoder_impl; }
GetEncoderFrameSize() const254   int GetEncoderFrameSize() const {
255     return GetEncoderFrameSizeMs() * GetSampleRateHz() / 1000;
256   }
257 };
258 
259 // Checks that the number of encoded and decoded samples match.
TEST_P(EncoderDecoderPairTest,EncodeDecode)260 TEST_P(EncoderDecoderPairTest, EncodeDecode) {
261   auto pcm_file = GetPcmTestFileReader(GetSampleRateHz());
262   auto encoder = CreateEncoder(GetEncoderIsacImpl(), GetSampleRateHz(),
263                                GetEncoderFrameSizeMs(), /*bitrate_bps=*/20000);
264   auto decoder = CreateDecoder(GetDecoderIsacImpl(), GetSampleRateHz());
265   const int encoder_frame_size = GetEncoderFrameSize();
266   std::vector<int16_t> out(encoder_frame_size);
267   size_t num_encoded_samples = 0;
268   size_t num_decoded_samples = 0;
269   constexpr int kNumFrames = 12;
270   for (int i = 0; i < kNumFrames; ++i) {
271     AudioFrame in;
272     pcm_file->Read10MsData(in);
273     rtc::Buffer encoded;
274     encoder->Encode(/*rtp_timestamp=*/0, AudioFrameToView(in), &encoded);
275     num_encoded_samples += in.samples_per_channel();
276     if (encoded.empty()) {
277       continue;
278     }
279     // Decode.
280     const std::vector<AudioDecoder::ParseResult> parse_result =
281         decoder->ParsePayload(std::move(encoded), /*timestamp=*/0);
282     EXPECT_EQ(parse_result.size(), size_t{1});
283     auto decode_result = parse_result[0].frame->Decode(out);
284     EXPECT_TRUE(decode_result.has_value());
285     EXPECT_EQ(out.size(), decode_result->num_decoded_samples);
286     num_decoded_samples += decode_result->num_decoded_samples;
287   }
288   EXPECT_EQ(num_encoded_samples, num_decoded_samples);
289 }
290 
291 // Creates tests for different encoder frame sizes and different
292 // encoder/decoder implementations.
293 INSTANTIATE_TEST_SUITE_P(
294     IsacApiTest,
295     EncoderDecoderPairTest,
__anon0525dd8c0502null296     ::testing::ValuesIn([] {
297       std::vector<EncoderDecoderPairTestParams> cases;
298       for (int frame_size_ms : {30, 60}) {
299         for (IsacImpl enc : {IsacImpl::kFloat, IsacImpl::kFixed}) {
300           for (IsacImpl dec : {IsacImpl::kFloat, IsacImpl::kFixed}) {
301             cases.push_back({16000, frame_size_ms, enc, dec});
302           }
303         }
304       }
305       cases.push_back({32000, 30, IsacImpl::kFloat, IsacImpl::kFloat});
306       return cases;
307     }()),
__anon0525dd8c0602(const ::testing::TestParamInfo<EncoderDecoderPairTestParams>& info) 308     [](const ::testing::TestParamInfo<EncoderDecoderPairTestParams>& info) {
309       rtc::StringBuilder b;
310       const auto& p = info.param;
311       b << p.sample_rate_hz << "_" << p.frame_size_ms << "_"
312         << IsacImplToString(p.encoder_impl) << "_"
313         << IsacImplToString(p.decoder_impl);
314       return b.Release();
315     });
316 
317 }  // namespace
318 }  // namespace webrtc
319