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