xref: /aosp_15_r20/external/webrtc/modules/audio_coding/test/TestVADDTX.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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