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 "webrtc/modules/audio_coding/main/test/TestVADDTX.h"
12
13#include <iostream>
14
15#include "webrtc/common_types.h"
16#include "webrtc/engine_configurations.h"
17#include "webrtc/modules/audio_coding/main/acm2/acm_common_defs.h"
18#include "webrtc/modules/audio_coding/main/interface/audio_coding_module_typedefs.h"
19#include "webrtc/modules/audio_coding/main/test/utility.h"
20#include "webrtc/system_wrappers/interface/trace.h"
21#include "webrtc/test/testsupport/fileutils.h"
22
23namespace webrtc {
24
25TestVADDTX::TestVADDTX()
26    : _acmA(AudioCodingModule::Create(0)),
27      _acmB(AudioCodingModule::Create(1)),
28      _channelA2B(NULL) {}
29
30TestVADDTX::~TestVADDTX() {
31  if (_channelA2B != NULL) {
32    delete _channelA2B;
33    _channelA2B = NULL;
34  }
35}
36
37void TestVADDTX::Perform() {
38  const std::string file_name = webrtc::test::ResourcePath(
39      "audio_coding/testfile32kHz", "pcm");
40  _inFileA.Open(file_name, 32000, "rb");
41
42  EXPECT_EQ(0, _acmA->InitializeReceiver());
43  EXPECT_EQ(0, _acmB->InitializeReceiver());
44
45  uint8_t numEncoders = _acmA->NumberOfCodecs();
46  CodecInst myCodecParam;
47  for (uint8_t n = 0; n < numEncoders; n++) {
48    EXPECT_EQ(0, _acmB->Codec(n, &myCodecParam));
49    if (!strcmp(myCodecParam.plname, "opus")) {
50      // Register Opus as mono.
51      myCodecParam.channels = 1;
52    }
53    EXPECT_EQ(0, _acmB->RegisterReceiveCodec(myCodecParam));
54  }
55
56  // Create and connect the channel
57  _channelA2B = new Channel;
58  _acmA->RegisterTransportCallback(_channelA2B);
59  _channelA2B->RegisterReceiverACM(_acmB.get());
60
61  _acmA->RegisterVADCallback(&_monitor);
62
63  int16_t testCntr = 1;
64
65#ifdef WEBRTC_CODEC_ISAC
66  // Open outputfile
67  OpenOutFile(testCntr++);
68
69  // Register iSAC WB as send codec
70  char nameISAC[] = "ISAC";
71  RegisterSendCodec('A', nameISAC, 16000);
72
73  // Run the five test cased
74  runTestCases();
75
76  // Close file
77  _outFileB.Close();
78
79  // Open outputfile
80  OpenOutFile(testCntr++);
81
82  // Register iSAC SWB as send codec
83  RegisterSendCodec('A', nameISAC, 32000);
84
85  // Run the five test cased
86  runTestCases();
87
88  // Close file
89  _outFileB.Close();
90#endif
91#ifdef WEBRTC_CODEC_ILBC
92  // Open outputfile
93  OpenOutFile(testCntr++);
94
95  // Register iLBC as send codec
96  char nameILBC[] = "ilbc";
97  RegisterSendCodec('A', nameILBC);
98
99  // Run the five test cased
100  runTestCases();
101
102  // Close file
103  _outFileB.Close();
104
105#endif
106#ifdef WEBRTC_CODEC_OPUS
107  // Open outputfile
108  OpenOutFile(testCntr++);
109
110  // Register Opus as send codec
111  char nameOPUS[] = "opus";
112  RegisterSendCodec('A', nameOPUS);
113
114  // Run the five test cased
115  runTestCases();
116
117  // Close file
118  _outFileB.Close();
119
120#endif
121}
122
123void TestVADDTX::runTestCases() {
124  // #1 DTX = OFF, VAD = ON, VADNormal
125  SetVAD(false, true, VADNormal);
126  Run();
127  VerifyTest();
128
129  // #2 DTX = OFF, VAD = ON, VADAggr
130  SetVAD(false, true, VADAggr);
131  Run();
132  VerifyTest();
133
134  // #3 DTX = ON, VAD = ON, VADLowBitrate
135  SetVAD(true, true, VADLowBitrate);
136  Run();
137  VerifyTest();
138
139  // #4 DTX = ON, VAD = ON, VADVeryAggr
140  SetVAD(true, true, VADVeryAggr);
141  Run();
142  VerifyTest();
143
144  // #5 DTX = ON, VAD = OFF, VADNormal
145  SetVAD(true, false, VADNormal);
146  Run();
147  VerifyTest();
148}
149
150void TestVADDTX::runTestInternalDTX(int expected_result) {
151  // #6 DTX = ON, VAD = ON, VADNormal
152  SetVAD(true, true, VADNormal);
153  EXPECT_EQ(expected_result, _acmA->ReplaceInternalDTXWithWebRtc(true));
154  if (expected_result == 0) {
155    Run();
156    VerifyTest();
157  }
158}
159
160void TestVADDTX::SetVAD(bool statusDTX, bool statusVAD, int16_t vadMode) {
161  bool dtxEnabled, vadEnabled;
162  ACMVADMode vadModeSet;
163
164  EXPECT_EQ(0, _acmA->SetVAD(statusDTX, statusVAD, (ACMVADMode) vadMode));
165  EXPECT_EQ(0, _acmA->VAD(&dtxEnabled, &vadEnabled, &vadModeSet));
166
167  // Requested VAD/DTX settings
168  _setStruct.statusDTX = statusDTX;
169  _setStruct.statusVAD = statusVAD;
170  _setStruct.vadMode = (ACMVADMode) vadMode;
171
172  // VAD settings after setting VAD in ACM
173  _getStruct.statusDTX = dtxEnabled;
174  _getStruct.statusVAD = vadEnabled;
175  _getStruct.vadMode = vadModeSet;
176}
177
178VADDTXstruct TestVADDTX::GetVAD() {
179  VADDTXstruct retStruct;
180  bool dtxEnabled, vadEnabled;
181  ACMVADMode vadModeSet;
182
183  EXPECT_EQ(0, _acmA->VAD(&dtxEnabled, &vadEnabled, &vadModeSet));
184
185  retStruct.statusDTX = dtxEnabled;
186  retStruct.statusVAD = vadEnabled;
187  retStruct.vadMode = vadModeSet;
188  return retStruct;
189}
190
191int16_t TestVADDTX::RegisterSendCodec(char side, char* codecName,
192                                      int32_t samplingFreqHz,
193                                      int32_t rateKbps) {
194  std::cout << std::flush;
195  AudioCodingModule* myACM;
196  switch (side) {
197    case 'A': {
198      myACM = _acmA.get();
199      break;
200    }
201    case 'B': {
202      myACM = _acmB.get();
203      break;
204    }
205    default:
206      return -1;
207  }
208
209  if (myACM == NULL) {
210    return -1;
211  }
212
213  CodecInst myCodecParam;
214  for (int16_t codecCntr = 0; codecCntr < myACM->NumberOfCodecs();
215      codecCntr++) {
216    EXPECT_EQ(0, myACM->Codec((uint8_t) codecCntr, &myCodecParam));
217    if (!STR_CASE_CMP(myCodecParam.plname, codecName)) {
218      if ((samplingFreqHz == -1) || (myCodecParam.plfreq == samplingFreqHz)) {
219        if ((rateKbps == -1) || (myCodecParam.rate == rateKbps)) {
220          break;
221        }
222      }
223    }
224  }
225
226  // We only allow VAD/DTX when sending mono.
227  myCodecParam.channels = 1;
228  EXPECT_EQ(0, myACM->RegisterSendCodec(myCodecParam));
229
230  // initialization was succesful
231  return 0;
232}
233
234void TestVADDTX::Run() {
235  AudioFrame audioFrame;
236
237  uint16_t SamplesIn10MsecA = _inFileA.PayloadLength10Ms();
238  uint32_t timestampA = 1;
239  int32_t outFreqHzB = _outFileB.SamplingFrequency();
240
241  while (!_inFileA.EndOfFile()) {
242    _inFileA.Read10MsData(audioFrame);
243    audioFrame.timestamp_ = timestampA;
244    timestampA += SamplesIn10MsecA;
245    EXPECT_EQ(0, _acmA->Add10MsData(audioFrame));
246    EXPECT_GT(_acmA->Process(), -1);
247    EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame));
248    _outFileB.Write10MsData(audioFrame.data_, audioFrame.samples_per_channel_);
249  }
250#ifdef PRINT_STAT
251  _monitor.PrintStatistics();
252#endif
253  _inFileA.Rewind();
254  _monitor.GetStatistics(_statCounter);
255  _monitor.ResetStatistics();
256}
257
258void TestVADDTX::OpenOutFile(int16_t test_number) {
259  std::string file_name;
260  std::stringstream file_stream;
261  file_stream << webrtc::test::OutputPath();
262  file_stream << "testVADDTX_outFile_";
263  file_stream << test_number << ".pcm";
264  file_name = file_stream.str();
265  _outFileB.Open(file_name, 16000, "wb");
266}
267
268int16_t TestVADDTX::VerifyTest() {
269  // Verify empty frame result
270  uint8_t statusEF = 0;
271  uint8_t vadPattern = 0;
272  uint8_t emptyFramePattern[6];
273  CodecInst myCodecParam;
274  _acmA->SendCodec(&myCodecParam);
275  bool dtxInUse = true;
276  bool isReplaced = false;
277  if ((STR_CASE_CMP(myCodecParam.plname, "G729") == 0)
278      || (STR_CASE_CMP(myCodecParam.plname, "G723") == 0)
279      || (STR_CASE_CMP(myCodecParam.plname, "AMR") == 0)
280      || (STR_CASE_CMP(myCodecParam.plname, "AMR-wb") == 0)
281      || (STR_CASE_CMP(myCodecParam.plname, "speex") == 0)) {
282    _acmA->IsInternalDTXReplacedWithWebRtc(&isReplaced);
283    if (!isReplaced) {
284      dtxInUse = false;
285    }
286  } else if (STR_CASE_CMP(myCodecParam.plname, "opus") == 0) {
287    if (_getStruct.statusDTX != false) {
288      // DTX status doesn't match expected.
289      vadPattern |= 4;
290    } else if (_getStruct.statusVAD != false) {
291      // Mismatch in VAD setting.
292      vadPattern |= 2;
293    } else {
294      _setStruct.statusDTX = false;
295      _setStruct.statusVAD = false;
296    }
297  }
298
299  // Check for error in VAD/DTX settings
300  if (_getStruct.statusDTX != _setStruct.statusDTX) {
301    // DTX status doesn't match expected
302    vadPattern |= 4;
303  }
304  if (_getStruct.statusDTX) {
305    if ((!_getStruct.statusVAD && dtxInUse)
306        || (!dtxInUse && (_getStruct.statusVAD != _setStruct.statusVAD))) {
307      // Missmatch in VAD setting
308      vadPattern |= 2;
309    }
310  } else {
311    if (_getStruct.statusVAD != _setStruct.statusVAD) {
312      // VAD status doesn't match expected
313      vadPattern |= 2;
314    }
315  }
316  if (_getStruct.vadMode != _setStruct.vadMode) {
317    // VAD Mode doesn't match expected
318    vadPattern |= 1;
319  }
320
321  // Set expected empty frame pattern
322  int ii;
323  for (ii = 0; ii < 6; ii++) {
324    emptyFramePattern[ii] = 0;
325  }
326  // 0 - "kNoEncoding", not important to check.
327  //      Codecs with packetsize != 80 samples will get this output.
328  // 1 - "kActiveNormalEncoded", expect to receive some frames with this label .
329  // 2 - "kPassiveNormalEncoded".
330  // 3 - "kPassiveDTXNB".
331  // 4 - "kPassiveDTXWB".
332  // 5 - "kPassiveDTXSWB".
333  emptyFramePattern[0] = 1;
334  emptyFramePattern[1] = 1;
335  emptyFramePattern[2] = (((!_getStruct.statusDTX && _getStruct.statusVAD)
336      || (!dtxInUse && _getStruct.statusDTX)));
337  emptyFramePattern[3] = ((_getStruct.statusDTX && dtxInUse
338      && (_acmA->SendFrequency() == 8000)));
339  emptyFramePattern[4] = ((_getStruct.statusDTX && dtxInUse
340      && (_acmA->SendFrequency() == 16000)));
341  emptyFramePattern[5] = ((_getStruct.statusDTX && dtxInUse
342      && (_acmA->SendFrequency() == 32000)));
343
344  // Check pattern 1-5 (skip 0)
345  for (int ii = 1; ii < 6; ii++) {
346    if (emptyFramePattern[ii]) {
347      statusEF |= (_statCounter[ii] == 0);
348    } else {
349      statusEF |= (_statCounter[ii] > 0);
350    }
351  }
352  EXPECT_EQ(0, statusEF);
353  EXPECT_EQ(0, vadPattern);
354
355  return 0;
356}
357
358ActivityMonitor::ActivityMonitor() {
359  _counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] =
360      _counter[5] = 0;
361}
362
363ActivityMonitor::~ActivityMonitor() {
364}
365
366int32_t ActivityMonitor::InFrameType(int16_t frameType) {
367  _counter[frameType]++;
368  return 0;
369}
370
371void ActivityMonitor::PrintStatistics() {
372  printf("\n");
373  printf("kActiveNormalEncoded  kPassiveNormalEncoded  kPassiveDTXWB  ");
374  printf("kPassiveDTXNB kPassiveDTXSWB kFrameEmpty\n");
375  printf("%19u", _counter[1]);
376  printf("%22u", _counter[2]);
377  printf("%14u", _counter[3]);
378  printf("%14u", _counter[4]);
379  printf("%14u", _counter[5]);
380  printf("%11u", _counter[0]);
381  printf("\n\n");
382}
383
384void ActivityMonitor::ResetStatistics() {
385  _counter[0] = _counter[1] = _counter[2] = _counter[3] = _counter[4] =
386      _counter[5] = 0;
387}
388
389void ActivityMonitor::GetStatistics(uint32_t* getCounter) {
390  for (int ii = 0; ii < 6; ii++) {
391    getCounter[ii] = _counter[ii];
392  }
393}
394
395}  // namespace webrtc
396