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/TestVADDTX.h"
12
13 #include <string>
14
15 #include "absl/strings/match.h"
16 #include "api/audio_codecs/audio_decoder_factory_template.h"
17 #include "api/audio_codecs/audio_encoder_factory_template.h"
18 #include "api/audio_codecs/ilbc/audio_decoder_ilbc.h"
19 #include "api/audio_codecs/ilbc/audio_encoder_ilbc.h"
20 #include "api/audio_codecs/isac/audio_decoder_isac_float.h"
21 #include "api/audio_codecs/isac/audio_encoder_isac_float.h"
22 #include "api/audio_codecs/opus/audio_decoder_opus.h"
23 #include "api/audio_codecs/opus/audio_encoder_opus.h"
24 #include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
25 #include "modules/audio_coding/test/PCMFile.h"
26 #include "rtc_base/strings/string_builder.h"
27 #include "test/gtest.h"
28 #include "test/testsupport/file_utils.h"
29
30 namespace webrtc {
31
MonitoringAudioPacketizationCallback(AudioPacketizationCallback * next)32 MonitoringAudioPacketizationCallback::MonitoringAudioPacketizationCallback(
33 AudioPacketizationCallback* next)
34 : next_(next) {
35 ResetStatistics();
36 }
37
SendData(AudioFrameType frame_type,uint8_t payload_type,uint32_t timestamp,const uint8_t * payload_data,size_t payload_len_bytes,int64_t absolute_capture_timestamp_ms)38 int32_t MonitoringAudioPacketizationCallback::SendData(
39 AudioFrameType frame_type,
40 uint8_t payload_type,
41 uint32_t timestamp,
42 const uint8_t* payload_data,
43 size_t payload_len_bytes,
44 int64_t absolute_capture_timestamp_ms) {
45 counter_[static_cast<int>(frame_type)]++;
46 return next_->SendData(frame_type, payload_type, timestamp, payload_data,
47 payload_len_bytes, absolute_capture_timestamp_ms);
48 }
49
PrintStatistics()50 void MonitoringAudioPacketizationCallback::PrintStatistics() {
51 printf("\n");
52 printf("kEmptyFrame %u\n",
53 counter_[static_cast<int>(AudioFrameType::kEmptyFrame)]);
54 printf("kAudioFrameSpeech %u\n",
55 counter_[static_cast<int>(AudioFrameType::kAudioFrameSpeech)]);
56 printf("kAudioFrameCN %u\n",
57 counter_[static_cast<int>(AudioFrameType::kAudioFrameCN)]);
58 printf("\n\n");
59 }
60
ResetStatistics()61 void MonitoringAudioPacketizationCallback::ResetStatistics() {
62 memset(counter_, 0, sizeof(counter_));
63 }
64
GetStatistics(uint32_t * counter)65 void MonitoringAudioPacketizationCallback::GetStatistics(uint32_t* counter) {
66 memcpy(counter, counter_, sizeof(counter_));
67 }
68
TestVadDtx()69 TestVadDtx::TestVadDtx()
70 : encoder_factory_(CreateAudioEncoderFactory<AudioEncoderIlbc,
71 AudioEncoderIsacFloat,
72 AudioEncoderOpus>()),
73 decoder_factory_(CreateAudioDecoderFactory<AudioDecoderIlbc,
74 AudioDecoderIsacFloat,
75 AudioDecoderOpus>()),
76 acm_send_(AudioCodingModule::Create(
77 AudioCodingModule::Config(decoder_factory_))),
78 acm_receive_(AudioCodingModule::Create(
79 AudioCodingModule::Config(decoder_factory_))),
80 channel_(std::make_unique<Channel>()),
81 packetization_callback_(
82 std::make_unique<MonitoringAudioPacketizationCallback>(
83 channel_.get())) {
84 EXPECT_EQ(
85 0, acm_send_->RegisterTransportCallback(packetization_callback_.get()));
86 channel_->RegisterReceiverACM(acm_receive_.get());
87 }
88
RegisterCodec(const SdpAudioFormat & codec_format,absl::optional<Vad::Aggressiveness> vad_mode)89 bool TestVadDtx::RegisterCodec(const SdpAudioFormat& codec_format,
90 absl::optional<Vad::Aggressiveness> vad_mode) {
91 constexpr int payload_type = 17, cn_payload_type = 117;
92 bool added_comfort_noise = false;
93
94 auto encoder = encoder_factory_->MakeAudioEncoder(payload_type, codec_format,
95 absl::nullopt);
96 if (vad_mode.has_value() &&
97 !absl::EqualsIgnoreCase(codec_format.name, "opus")) {
98 AudioEncoderCngConfig config;
99 config.speech_encoder = std::move(encoder);
100 config.num_channels = 1;
101 config.payload_type = cn_payload_type;
102 config.vad_mode = vad_mode.value();
103 encoder = CreateComfortNoiseEncoder(std::move(config));
104 added_comfort_noise = true;
105 }
106 channel_->SetIsStereo(encoder->NumChannels() > 1);
107 acm_send_->SetEncoder(std::move(encoder));
108
109 std::map<int, SdpAudioFormat> receive_codecs = {{payload_type, codec_format}};
110 acm_receive_->SetReceiveCodecs(receive_codecs);
111
112 return added_comfort_noise;
113 }
114
115 // Encoding a file and see if the numbers that various packets occur follow
116 // the expectation.
Run(std::string in_filename,int frequency,int channels,std::string out_filename,bool append,const int * expects)117 void TestVadDtx::Run(std::string in_filename,
118 int frequency,
119 int channels,
120 std::string out_filename,
121 bool append,
122 const int* expects) {
123 packetization_callback_->ResetStatistics();
124
125 PCMFile in_file;
126 in_file.Open(in_filename, frequency, "rb");
127 in_file.ReadStereo(channels > 1);
128 // Set test length to 1000 ms (100 blocks of 10 ms each).
129 in_file.SetNum10MsBlocksToRead(100);
130 // Fast-forward both files 500 ms (50 blocks). The first second of the file is
131 // silence, but we want to keep half of that to test silence periods.
132 in_file.FastForward(50);
133
134 PCMFile out_file;
135 if (append) {
136 out_file.Open(out_filename, kOutputFreqHz, "ab");
137 } else {
138 out_file.Open(out_filename, kOutputFreqHz, "wb");
139 }
140
141 uint16_t frame_size_samples = in_file.PayloadLength10Ms();
142 AudioFrame audio_frame;
143 while (!in_file.EndOfFile()) {
144 in_file.Read10MsData(audio_frame);
145 audio_frame.timestamp_ = time_stamp_;
146 time_stamp_ += frame_size_samples;
147 EXPECT_GE(acm_send_->Add10MsData(audio_frame), 0);
148 bool muted;
149 acm_receive_->PlayoutData10Ms(kOutputFreqHz, &audio_frame, &muted);
150 ASSERT_FALSE(muted);
151 out_file.Write10MsData(audio_frame);
152 }
153
154 in_file.Close();
155 out_file.Close();
156
157 #ifdef PRINT_STAT
158 packetization_callback_->PrintStatistics();
159 #endif
160
161 uint32_t stats[3];
162 packetization_callback_->GetStatistics(stats);
163 packetization_callback_->ResetStatistics();
164
165 for (const auto& st : stats) {
166 int i = &st - stats; // Calculate the current position in stats.
167 switch (expects[i]) {
168 case 0: {
169 EXPECT_EQ(0u, st) << "stats[" << i << "] error. Output file "
170 << out_filename;
171 break;
172 }
173 case 1: {
174 EXPECT_GT(st, 0u) << "stats[" << i << "] error. Output file "
175 << out_filename;
176 break;
177 }
178 }
179 }
180 }
181
182 // Following is the implementation of TestWebRtcVadDtx.
TestWebRtcVadDtx()183 TestWebRtcVadDtx::TestWebRtcVadDtx() : output_file_num_(0) {}
184
Perform()185 void TestWebRtcVadDtx::Perform() {
186 RunTestCases({"ISAC", 16000, 1});
187 RunTestCases({"ISAC", 32000, 1});
188 RunTestCases({"ILBC", 8000, 1});
189 RunTestCases({"opus", 48000, 2});
190 }
191
192 // Test various configurations on VAD/DTX.
RunTestCases(const SdpAudioFormat & codec_format)193 void TestWebRtcVadDtx::RunTestCases(const SdpAudioFormat& codec_format) {
194 RegisterCodec(codec_format, absl::nullopt);
195 Test(/*new_outfile=*/true,
196 /*expect_vad_packets=*/codec_format.name == "opus");
197
198 RegisterCodec(codec_format, Vad::kVadAggressive);
199 Test(/*new_outfile=*/false,
200 /*expect_vad_packets=*/true);
201
202 RegisterCodec(codec_format, Vad::kVadLowBitrate);
203 Test(/*new_outfile=*/false,
204 /*expect_vad_packets=*/true);
205
206 RegisterCodec(codec_format, Vad::kVadVeryAggressive);
207 Test(/*new_outfile=*/false, /*expect_vad_packets=*/true);
208
209 RegisterCodec(codec_format, Vad::kVadNormal);
210 Test(/*new_outfile=*/false,
211 /*expect_vad_packets=*/true);
212 }
213
214 // Set the expectation and run the test.
Test(bool new_outfile,bool expect_vad_packets)215 void TestWebRtcVadDtx::Test(bool new_outfile, bool expect_vad_packets) {
216 int expects[] = {-1, 1, expect_vad_packets ? 1 : -1, 0, 0};
217 if (new_outfile) {
218 output_file_num_++;
219 }
220 rtc::StringBuilder out_filename;
221 out_filename << webrtc::test::OutputPath() << "testWebRtcVadDtx_outFile_"
222 << output_file_num_ << ".pcm";
223 Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
224 out_filename.str(), !new_outfile, expects);
225 }
226
227 // Following is the implementation of TestOpusDtx.
Perform()228 void TestOpusDtx::Perform() {
229 int expects[] = {0, 0, 0, 0, 0};
230
231 // Register Opus as send codec
232 std::string out_filename =
233 webrtc::test::OutputPath() + "testOpusDtx_outFile_mono.pcm";
234 RegisterCodec({"opus", 48000, 2}, absl::nullopt);
235
236 acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
237 (*encoder_ptr)->SetDtx(false);
238 });
239
240 expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 0;
241 expects[static_cast<int>(AudioFrameType::kAudioFrameSpeech)] = 1;
242 expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1;
243 Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
244 out_filename, false, expects);
245
246 acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
247 (*encoder_ptr)->SetDtx(true);
248 });
249 expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1;
250 expects[static_cast<int>(AudioFrameType::kAudioFrameSpeech)] = 1;
251 expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1;
252 Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
253 out_filename, true, expects);
254
255 // Register stereo Opus as send codec
256 out_filename = webrtc::test::OutputPath() + "testOpusDtx_outFile_stereo.pcm";
257 RegisterCodec({"opus", 48000, 2, {{"stereo", "1"}}}, absl::nullopt);
258
259 acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
260 (*encoder_ptr)->SetDtx(false);
261 });
262 expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 0;
263 expects[static_cast<int>(AudioFrameType::kAudioFrameSpeech)] = 1;
264 expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 0;
265 Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000,
266 2, out_filename, false, expects);
267
268 acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
269 (*encoder_ptr)->SetDtx(true);
270 // The default bitrate will not generate frames recognized as CN on desktop
271 // since the frames will be encoded as CELT. Set a low target bitrate to get
272 // consistent behaviour across platforms.
273 (*encoder_ptr)->OnReceivedTargetAudioBitrate(24000);
274 });
275
276 expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1;
277 expects[static_cast<int>(AudioFrameType::kAudioFrameSpeech)] = 1;
278 expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1;
279 Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000,
280 2, out_filename, true, expects);
281 }
282
283 } // namespace webrtc
284