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.";
170         break;
171       }
172       case 1: {
173         EXPECT_GT(st, 0u) << "stats[" << i << "] error.";
174         break;
175       }
176     }
177   }
178 }
179 
180 // Following is the implementation of TestWebRtcVadDtx.
TestWebRtcVadDtx()181 TestWebRtcVadDtx::TestWebRtcVadDtx() : output_file_num_(0) {}
182 
Perform()183 void TestWebRtcVadDtx::Perform() {
184   RunTestCases({"ISAC", 16000, 1});
185   RunTestCases({"ISAC", 32000, 1});
186   RunTestCases({"ILBC", 8000, 1});
187   RunTestCases({"opus", 48000, 2});
188 }
189 
190 // Test various configurations on VAD/DTX.
RunTestCases(const SdpAudioFormat & codec_format)191 void TestWebRtcVadDtx::RunTestCases(const SdpAudioFormat& codec_format) {
192   Test(/*new_outfile=*/true,
193        /*expect_dtx_enabled=*/RegisterCodec(codec_format, absl::nullopt));
194 
195   Test(/*new_outfile=*/false,
196        /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadAggressive));
197 
198   Test(/*new_outfile=*/false,
199        /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadLowBitrate));
200 
201   Test(/*new_outfile=*/false, /*expect_dtx_enabled=*/RegisterCodec(
202            codec_format, Vad::kVadVeryAggressive));
203 
204   Test(/*new_outfile=*/false,
205        /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadNormal));
206 }
207 
208 // Set the expectation and run the test.
Test(bool new_outfile,bool expect_dtx_enabled)209 void TestWebRtcVadDtx::Test(bool new_outfile, bool expect_dtx_enabled) {
210   int expects[] = {-1, 1, expect_dtx_enabled, 0, 0};
211   if (new_outfile) {
212     output_file_num_++;
213   }
214   rtc::StringBuilder out_filename;
215   out_filename << webrtc::test::OutputPath() << "testWebRtcVadDtx_outFile_"
216                << output_file_num_ << ".pcm";
217   Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
218       out_filename.str(), !new_outfile, expects);
219 }
220 
221 // Following is the implementation of TestOpusDtx.
Perform()222 void TestOpusDtx::Perform() {
223   int expects[] = {0, 1, 0, 0, 0};
224 
225   // Register Opus as send codec
226   std::string out_filename =
227       webrtc::test::OutputPath() + "testOpusDtx_outFile_mono.pcm";
228   RegisterCodec({"opus", 48000, 2}, absl::nullopt);
229   acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
230     (*encoder_ptr)->SetDtx(false);
231   });
232 
233   Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
234       out_filename, false, expects);
235 
236   acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
237     (*encoder_ptr)->SetDtx(true);
238   });
239   expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1;
240   expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1;
241   Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1,
242       out_filename, true, expects);
243 
244   // Register stereo Opus as send codec
245   out_filename = webrtc::test::OutputPath() + "testOpusDtx_outFile_stereo.pcm";
246   RegisterCodec({"opus", 48000, 2, {{"stereo", "1"}}}, absl::nullopt);
247   acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
248     (*encoder_ptr)->SetDtx(false);
249   });
250   expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 0;
251   expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 0;
252   Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000,
253       2, out_filename, false, expects);
254 
255   acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) {
256     (*encoder_ptr)->SetDtx(true);
257     // The default bitrate will not generate frames recognized as CN on desktop
258     // since the frames will be encoded as CELT. Set a low target bitrate to get
259     // consistent behaviour across platforms.
260     (*encoder_ptr)->OnReceivedTargetAudioBitrate(24000);
261   });
262 
263   expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1;
264   expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1;
265   Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000,
266       2, out_filename, true, expects);
267 }
268 
269 }  // namespace webrtc
270