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