• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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