1 /*
2 * Copyright (c) 2012 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 "modules/audio_coding/test/iSACTest.h"
12
13 #include <ctype.h>
14 #include <stdio.h>
15 #include <string.h>
16
17 #include "absl/strings/match.h"
18 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
19 #include "api/audio_codecs/isac/audio_encoder_isac_float.h"
20 #include "rtc_base/strings/string_builder.h"
21 #include "rtc_base/time_utils.h"
22 #include "system_wrappers/include/sleep.h"
23 #include "test/gmock.h"
24 #include "test/gtest.h"
25 #include "test/testsupport/file_utils.h"
26
27 namespace webrtc {
28
29 using ::testing::AnyOf;
30 using ::testing::Eq;
31 using ::testing::StrCaseEq;
32
33 namespace {
34
35 constexpr int kISAC16kPayloadType = 103;
36 constexpr int kISAC32kPayloadType = 104;
37 const SdpAudioFormat kISAC16kFormat = {"ISAC", 16000, 1};
38 const SdpAudioFormat kISAC32kFormat = {"ISAC", 32000, 1};
39
TweakConfig(AudioEncoderIsacFloat::Config config,const ACMTestISACConfig & test_config)40 AudioEncoderIsacFloat::Config TweakConfig(
41 AudioEncoderIsacFloat::Config config,
42 const ACMTestISACConfig& test_config) {
43 if (test_config.currentRateBitPerSec > 0) {
44 config.bit_rate = test_config.currentRateBitPerSec;
45 }
46 if (test_config.currentFrameSizeMsec != 0) {
47 config.frame_size_ms = test_config.currentFrameSizeMsec;
48 }
49 EXPECT_THAT(config.IsOk(), Eq(true));
50 return config;
51 }
52
SetISACConfigDefault(ACMTestISACConfig & isacConfig)53 void SetISACConfigDefault(ACMTestISACConfig& isacConfig) {
54 isacConfig.currentRateBitPerSec = 0;
55 isacConfig.currentFrameSizeMsec = 0;
56 isacConfig.encodingMode = -1;
57 isacConfig.initRateBitPerSec = 0;
58 isacConfig.initFrameSizeInMsec = 0;
59 isacConfig.enforceFrameSize = false;
60 }
61
62 } // namespace
63
ACMTestTimer()64 ISACTest::ACMTestTimer::ACMTestTimer() : _msec(0), _sec(0), _min(0), _hour(0) {
65 return;
66 }
67
~ACMTestTimer()68 ISACTest::ACMTestTimer::~ACMTestTimer() {
69 return;
70 }
71
Reset()72 void ISACTest::ACMTestTimer::Reset() {
73 _msec = 0;
74 _sec = 0;
75 _min = 0;
76 _hour = 0;
77 return;
78 }
Tick10ms()79 void ISACTest::ACMTestTimer::Tick10ms() {
80 _msec += 10;
81 Adjust();
82 return;
83 }
84
Tick1ms()85 void ISACTest::ACMTestTimer::Tick1ms() {
86 _msec++;
87 Adjust();
88 return;
89 }
90
Tick100ms()91 void ISACTest::ACMTestTimer::Tick100ms() {
92 _msec += 100;
93 Adjust();
94 return;
95 }
96
Tick1sec()97 void ISACTest::ACMTestTimer::Tick1sec() {
98 _sec++;
99 Adjust();
100 return;
101 }
102
CurrentTimeHMS(char * currTime)103 void ISACTest::ACMTestTimer::CurrentTimeHMS(char* currTime) {
104 sprintf(currTime, "%4lu:%02u:%06.3f", _hour, _min,
105 (double)_sec + (double)_msec / 1000.);
106 return;
107 }
108
CurrentTime(unsigned long & h,unsigned char & m,unsigned char & s,unsigned short & ms)109 void ISACTest::ACMTestTimer::CurrentTime(unsigned long& h,
110 unsigned char& m,
111 unsigned char& s,
112 unsigned short& ms) {
113 h = _hour;
114 m = _min;
115 s = _sec;
116 ms = _msec;
117 return;
118 }
119
Adjust()120 void ISACTest::ACMTestTimer::Adjust() {
121 unsigned int n;
122 if (_msec >= 1000) {
123 n = _msec / 1000;
124 _msec -= (1000 * n);
125 _sec += n;
126 }
127 if (_sec >= 60) {
128 n = _sec / 60;
129 _sec -= (n * 60);
130 _min += n;
131 }
132 if (_min >= 60) {
133 n = _min / 60;
134 _min -= (n * 60);
135 _hour += n;
136 }
137 }
138
ISACTest()139 ISACTest::ISACTest()
140 : _acmA(AudioCodingModule::Create(
141 AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))),
142 _acmB(AudioCodingModule::Create(
143 AudioCodingModule::Config(CreateBuiltinAudioDecoderFactory()))) {}
144
~ISACTest()145 ISACTest::~ISACTest() {}
146
Setup()147 void ISACTest::Setup() {
148 // Register both iSAC-wb & iSAC-swb in both sides as receiver codecs.
149 std::map<int, SdpAudioFormat> receive_codecs = {
150 {kISAC16kPayloadType, kISAC16kFormat},
151 {kISAC32kPayloadType, kISAC32kFormat}};
152 _acmA->SetReceiveCodecs(receive_codecs);
153 _acmB->SetReceiveCodecs(receive_codecs);
154
155 //--- Set A-to-B channel
156 _channel_A2B.reset(new Channel);
157 EXPECT_EQ(0, _acmA->RegisterTransportCallback(_channel_A2B.get()));
158 _channel_A2B->RegisterReceiverACM(_acmB.get());
159
160 //--- Set B-to-A channel
161 _channel_B2A.reset(new Channel);
162 EXPECT_EQ(0, _acmB->RegisterTransportCallback(_channel_B2A.get()));
163 _channel_B2A->RegisterReceiverACM(_acmA.get());
164
165 file_name_swb_ =
166 webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
167
168 _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
169 *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat),
170 kISAC16kPayloadType));
171 _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
172 *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat),
173 kISAC32kPayloadType));
174
175 _inFileA.Open(file_name_swb_, 32000, "rb");
176 // Set test length to 500 ms (50 blocks of 10 ms each).
177 _inFileA.SetNum10MsBlocksToRead(50);
178 // Fast-forward 1 second (100 blocks) since the files start with silence.
179 _inFileA.FastForward(100);
180 std::string fileNameA = webrtc::test::OutputPath() + "testisac_a.pcm";
181 std::string fileNameB = webrtc::test::OutputPath() + "testisac_b.pcm";
182 _outFileA.Open(fileNameA, 32000, "wb");
183 _outFileB.Open(fileNameB, 32000, "wb");
184
185 while (!_inFileA.EndOfFile()) {
186 Run10ms();
187 }
188
189 _inFileA.Close();
190 _outFileA.Close();
191 _outFileB.Close();
192 }
193
Perform()194 void ISACTest::Perform() {
195 Setup();
196
197 int16_t testNr = 0;
198 ACMTestISACConfig wbISACConfig;
199 ACMTestISACConfig swbISACConfig;
200
201 SetISACConfigDefault(wbISACConfig);
202 SetISACConfigDefault(swbISACConfig);
203
204 wbISACConfig.currentRateBitPerSec = -1;
205 swbISACConfig.currentRateBitPerSec = -1;
206 testNr++;
207 EncodeDecode(testNr, wbISACConfig, swbISACConfig);
208
209 SetISACConfigDefault(wbISACConfig);
210 SetISACConfigDefault(swbISACConfig);
211 testNr++;
212 EncodeDecode(testNr, wbISACConfig, swbISACConfig);
213
214 testNr++;
215 SwitchingSamplingRate(testNr, 4);
216 }
217
Run10ms()218 void ISACTest::Run10ms() {
219 AudioFrame audioFrame;
220 EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0);
221 EXPECT_GE(_acmA->Add10MsData(audioFrame), 0);
222 EXPECT_GE(_acmB->Add10MsData(audioFrame), 0);
223 bool muted;
224 EXPECT_EQ(0, _acmA->PlayoutData10Ms(32000, &audioFrame, &muted));
225 ASSERT_FALSE(muted);
226 _outFileA.Write10MsData(audioFrame);
227 EXPECT_EQ(0, _acmB->PlayoutData10Ms(32000, &audioFrame, &muted));
228 ASSERT_FALSE(muted);
229 _outFileB.Write10MsData(audioFrame);
230 }
231
EncodeDecode(int testNr,ACMTestISACConfig & wbISACConfig,ACMTestISACConfig & swbISACConfig)232 void ISACTest::EncodeDecode(int testNr,
233 ACMTestISACConfig& wbISACConfig,
234 ACMTestISACConfig& swbISACConfig) {
235 // Files in Side A and B
236 _inFileA.Open(file_name_swb_, 32000, "rb", true);
237 _inFileB.Open(file_name_swb_, 32000, "rb", true);
238
239 std::string file_name_out;
240 rtc::StringBuilder file_stream_a;
241 rtc::StringBuilder file_stream_b;
242 file_stream_a << webrtc::test::OutputPath();
243 file_stream_b << webrtc::test::OutputPath();
244 file_stream_a << "out_iSACTest_A_" << testNr << ".pcm";
245 file_stream_b << "out_iSACTest_B_" << testNr << ".pcm";
246 file_name_out = file_stream_a.str();
247 _outFileA.Open(file_name_out, 32000, "wb");
248 file_name_out = file_stream_b.str();
249 _outFileB.Open(file_name_out, 32000, "wb");
250
251 // Side A is sending super-wideband, and side B is sending wideband.
252 _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
253 TweakConfig(*AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat),
254 swbISACConfig),
255 kISAC32kPayloadType));
256 _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
257 TweakConfig(*AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat),
258 wbISACConfig),
259 kISAC16kPayloadType));
260
261 bool adaptiveMode = false;
262 if ((swbISACConfig.currentRateBitPerSec == -1) ||
263 (wbISACConfig.currentRateBitPerSec == -1)) {
264 adaptiveMode = true;
265 }
266 _myTimer.Reset();
267 _channel_A2B->ResetStats();
268 _channel_B2A->ResetStats();
269
270 char currentTime[500];
271 while (!(_inFileA.EndOfFile() || _inFileA.Rewinded())) {
272 Run10ms();
273 _myTimer.Tick10ms();
274 _myTimer.CurrentTimeHMS(currentTime);
275 }
276
277 _channel_A2B->ResetStats();
278 _channel_B2A->ResetStats();
279
280 _outFileA.Close();
281 _outFileB.Close();
282 _inFileA.Close();
283 _inFileB.Close();
284 }
285
SwitchingSamplingRate(int testNr,int maxSampRateChange)286 void ISACTest::SwitchingSamplingRate(int testNr, int maxSampRateChange) {
287 // Files in Side A
288 _inFileA.Open(file_name_swb_, 32000, "rb");
289 _inFileB.Open(file_name_swb_, 32000, "rb");
290
291 std::string file_name_out;
292 rtc::StringBuilder file_stream_a;
293 rtc::StringBuilder file_stream_b;
294 file_stream_a << webrtc::test::OutputPath();
295 file_stream_b << webrtc::test::OutputPath();
296 file_stream_a << "out_iSACTest_A_" << testNr << ".pcm";
297 file_stream_b << "out_iSACTest_B_" << testNr << ".pcm";
298 file_name_out = file_stream_a.str();
299 _outFileA.Open(file_name_out, 32000, "wb");
300 file_name_out = file_stream_b.str();
301 _outFileB.Open(file_name_out, 32000, "wb");
302
303 // Start with side A sending super-wideband and side B seding wideband.
304 // Toggle sending wideband/super-wideband in this test.
305 _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
306 *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat),
307 kISAC32kPayloadType));
308 _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
309 *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat),
310 kISAC16kPayloadType));
311
312 int numSendCodecChanged = 0;
313 _myTimer.Reset();
314 char currentTime[50];
315 while (numSendCodecChanged < (maxSampRateChange << 1)) {
316 Run10ms();
317 _myTimer.Tick10ms();
318 _myTimer.CurrentTimeHMS(currentTime);
319 if (_inFileA.EndOfFile()) {
320 if (_inFileA.SamplingFrequency() == 16000) {
321 // Switch side A to send super-wideband.
322 _inFileA.Close();
323 _inFileA.Open(file_name_swb_, 32000, "rb");
324 _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
325 *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat),
326 kISAC32kPayloadType));
327 } else {
328 // Switch side A to send wideband.
329 _inFileA.Close();
330 _inFileA.Open(file_name_swb_, 32000, "rb");
331 _acmA->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
332 *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat),
333 kISAC16kPayloadType));
334 }
335 numSendCodecChanged++;
336 }
337
338 if (_inFileB.EndOfFile()) {
339 if (_inFileB.SamplingFrequency() == 16000) {
340 // Switch side B to send super-wideband.
341 _inFileB.Close();
342 _inFileB.Open(file_name_swb_, 32000, "rb");
343 _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
344 *AudioEncoderIsacFloat::SdpToConfig(kISAC32kFormat),
345 kISAC32kPayloadType));
346 } else {
347 // Switch side B to send wideband.
348 _inFileB.Close();
349 _inFileB.Open(file_name_swb_, 32000, "rb");
350 _acmB->SetEncoder(AudioEncoderIsacFloat::MakeAudioEncoder(
351 *AudioEncoderIsacFloat::SdpToConfig(kISAC16kFormat),
352 kISAC16kPayloadType));
353 }
354 numSendCodecChanged++;
355 }
356 }
357 _outFileA.Close();
358 _outFileB.Close();
359 _inFileA.Close();
360 _inFileB.Close();
361 }
362
363 } // namespace webrtc
364