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