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/TestRedFec.h"
12 
13 #include <memory>
14 #include <utility>
15 
16 #include "absl/strings/match.h"
17 #include "api/audio_codecs/L16/audio_decoder_L16.h"
18 #include "api/audio_codecs/L16/audio_encoder_L16.h"
19 #include "api/audio_codecs/audio_decoder_factory_template.h"
20 #include "api/audio_codecs/audio_encoder_factory_template.h"
21 #include "api/audio_codecs/g711/audio_decoder_g711.h"
22 #include "api/audio_codecs/g711/audio_encoder_g711.h"
23 #include "api/audio_codecs/g722/audio_decoder_g722.h"
24 #include "api/audio_codecs/g722/audio_encoder_g722.h"
25 #include "api/audio_codecs/isac/audio_decoder_isac_float.h"
26 #include "api/audio_codecs/isac/audio_encoder_isac_float.h"
27 #include "api/audio_codecs/opus/audio_decoder_opus.h"
28 #include "api/audio_codecs/opus/audio_encoder_opus.h"
29 #include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
30 #include "modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
31 #include "modules/audio_coding/include/audio_coding_module_typedefs.h"
32 #include "rtc_base/strings/string_builder.h"
33 #include "test/gtest.h"
34 #include "test/testsupport/file_utils.h"
35 
36 namespace webrtc {
37 
TestRedFec()38 TestRedFec::TestRedFec()
39     : encoder_factory_(CreateAudioEncoderFactory<AudioEncoderG711,
40                                                  AudioEncoderG722,
41                                                  AudioEncoderIsacFloat,
42                                                  AudioEncoderL16,
43                                                  AudioEncoderOpus>()),
44       decoder_factory_(CreateAudioDecoderFactory<AudioDecoderG711,
45                                                  AudioDecoderG722,
46                                                  AudioDecoderIsacFloat,
47                                                  AudioDecoderL16,
48                                                  AudioDecoderOpus>()),
49       _acmA(AudioCodingModule::Create(
50           AudioCodingModule::Config(decoder_factory_))),
51       _acmB(AudioCodingModule::Create(
52           AudioCodingModule::Config(decoder_factory_))),
53       _channelA2B(NULL),
54       _testCntr(0) {}
55 
~TestRedFec()56 TestRedFec::~TestRedFec() {
57   if (_channelA2B != NULL) {
58     delete _channelA2B;
59     _channelA2B = NULL;
60   }
61 }
62 
Perform()63 void TestRedFec::Perform() {
64   const std::string file_name =
65       webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
66   _inFileA.Open(file_name, 32000, "rb");
67 
68   ASSERT_EQ(0, _acmA->InitializeReceiver());
69   ASSERT_EQ(0, _acmB->InitializeReceiver());
70 
71   // Create and connect the channel
72   _channelA2B = new Channel;
73   _acmA->RegisterTransportCallback(_channelA2B);
74   _channelA2B->RegisterReceiverACM(_acmB.get());
75 
76   RegisterSendCodec(_acmA, {"L16", 8000, 1}, Vad::kVadAggressive, true);
77 
78   OpenOutFile(_testCntr);
79   Run();
80   _outFileB.Close();
81 
82   // Switch to another 8 kHz codec; RED should remain switched on.
83   RegisterSendCodec(_acmA, {"PCMU", 8000, 1}, Vad::kVadAggressive, true);
84   OpenOutFile(_testCntr);
85   Run();
86   _outFileB.Close();
87 
88   // Switch to a 16 kHz codec; RED should be switched off.
89   RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false);
90 
91   OpenOutFile(_testCntr);
92   RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false);
93   Run();
94   RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false);
95   Run();
96   _outFileB.Close();
97 
98   RegisterSendCodec(_acmA, {"ISAC", 16000, 1}, Vad::kVadVeryAggressive, false);
99   OpenOutFile(_testCntr);
100   Run();
101   _outFileB.Close();
102 
103   // Switch to a 32 kHz codec; RED should be switched off.
104   RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, Vad::kVadVeryAggressive, false);
105   OpenOutFile(_testCntr);
106   Run();
107   _outFileB.Close();
108 
109   RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, absl::nullopt, false);
110 
111   _channelA2B->SetFECTestWithPacketLoss(true);
112   // Following tests are under packet losses.
113 
114   // Switch to a 16 kHz codec; RED should be switched off.
115   RegisterSendCodec(_acmA, {"G722", 8000, 1}, Vad::kVadAggressive, false);
116 
117   OpenOutFile(_testCntr);
118   Run();
119   _outFileB.Close();
120 
121   // Switch to a 16 kHz codec, RED should have been switched off.
122   RegisterSendCodec(_acmA, {"ISAC", 16000, 1}, Vad::kVadVeryAggressive, false);
123 
124   OpenOutFile(_testCntr);
125   Run();
126   _outFileB.Close();
127 
128   // Switch to a 32 kHz codec, RED should have been switched off.
129   RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, Vad::kVadVeryAggressive, false);
130 
131   OpenOutFile(_testCntr);
132   Run();
133   _outFileB.Close();
134 
135   RegisterSendCodec(_acmA, {"ISAC", 32000, 1}, absl::nullopt, false);
136 
137   RegisterSendCodec(_acmA, {"opus", 48000, 2}, absl::nullopt, false);
138 
139   // _channelA2B imposes 25% packet loss rate.
140   EXPECT_EQ(0, _acmA->SetPacketLossRate(25));
141 
142   _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) {
143     EXPECT_EQ(true, (*enc)->SetFec(true));
144   });
145 
146   OpenOutFile(_testCntr);
147   Run();
148 
149   // Switch to L16 with RED.
150   RegisterSendCodec(_acmA, {"L16", 8000, 1}, absl::nullopt, true);
151   Run();
152 
153   // Switch to Opus again.
154   RegisterSendCodec(_acmA, {"opus", 48000, 2}, absl::nullopt, false);
155   _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) {
156     EXPECT_EQ(true, (*enc)->SetFec(false));
157   });
158   Run();
159 
160   _acmA->ModifyEncoder([&](std::unique_ptr<AudioEncoder>* enc) {
161     EXPECT_EQ(true, (*enc)->SetFec(true));
162   });
163   _outFileB.Close();
164 }
165 
RegisterSendCodec(const std::unique_ptr<AudioCodingModule> & acm,const SdpAudioFormat & codec_format,absl::optional<Vad::Aggressiveness> vad_mode,bool use_red)166 void TestRedFec::RegisterSendCodec(
167     const std::unique_ptr<AudioCodingModule>& acm,
168     const SdpAudioFormat& codec_format,
169     absl::optional<Vad::Aggressiveness> vad_mode,
170     bool use_red) {
171   constexpr int payload_type = 17, cn_payload_type = 27, red_payload_type = 37;
172   const auto& other_acm = &acm == &_acmA ? _acmB : _acmA;
173 
174   auto encoder = encoder_factory_->MakeAudioEncoder(payload_type, codec_format,
175                                                     absl::nullopt);
176   EXPECT_NE(encoder, nullptr);
177   std::map<int, SdpAudioFormat> receive_codecs = {{payload_type, codec_format}};
178   if (!absl::EqualsIgnoreCase(codec_format.name, "opus")) {
179     if (vad_mode.has_value()) {
180       AudioEncoderCngConfig config;
181       config.speech_encoder = std::move(encoder);
182       config.num_channels = 1;
183       config.payload_type = cn_payload_type;
184       config.vad_mode = vad_mode.value();
185       encoder = CreateComfortNoiseEncoder(std::move(config));
186       receive_codecs.emplace(std::make_pair(
187           cn_payload_type, SdpAudioFormat("CN", codec_format.clockrate_hz, 1)));
188     }
189     if (use_red) {
190       AudioEncoderCopyRed::Config config;
191       config.payload_type = red_payload_type;
192       config.speech_encoder = std::move(encoder);
193       encoder = std::make_unique<AudioEncoderCopyRed>(std::move(config));
194       receive_codecs.emplace(
195           std::make_pair(red_payload_type,
196                          SdpAudioFormat("red", codec_format.clockrate_hz, 1)));
197     }
198   }
199   acm->SetEncoder(std::move(encoder));
200   other_acm->SetReceiveCodecs(receive_codecs);
201 }
202 
Run()203 void TestRedFec::Run() {
204   AudioFrame audioFrame;
205   int32_t outFreqHzB = _outFileB.SamplingFrequency();
206   // Set test length to 500 ms (50 blocks of 10 ms each).
207   _inFileA.SetNum10MsBlocksToRead(50);
208   // Fast-forward 1 second (100 blocks) since the file starts with silence.
209   _inFileA.FastForward(100);
210 
211   while (!_inFileA.EndOfFile()) {
212     EXPECT_GT(_inFileA.Read10MsData(audioFrame), 0);
213     EXPECT_GE(_acmA->Add10MsData(audioFrame), 0);
214     bool muted;
215     EXPECT_EQ(0, _acmB->PlayoutData10Ms(outFreqHzB, &audioFrame, &muted));
216     ASSERT_FALSE(muted);
217     _outFileB.Write10MsData(audioFrame.data(), audioFrame.samples_per_channel_);
218   }
219   _inFileA.Rewind();
220 }
221 
OpenOutFile(int16_t test_number)222 void TestRedFec::OpenOutFile(int16_t test_number) {
223   std::string file_name;
224   rtc::StringBuilder file_stream;
225   file_stream << webrtc::test::OutputPath();
226   file_stream << "TestRedFec_outFile_";
227   file_stream << test_number << ".pcm";
228   file_name = file_stream.str();
229   _outFileB.Open(file_name, 16000, "wb");
230 }
231 
232 }  // namespace webrtc
233