diff options
Diffstat (limited to 'third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc')
-rw-r--r-- | third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc new file mode 100644 index 0000000000..cb05deb92a --- /dev/null +++ b/third_party/libwebrtc/modules/audio_coding/test/TestVADDTX.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/audio_coding/test/TestVADDTX.h" + +#include <string> + +#include "absl/strings/match.h" +#include "absl/strings/string_view.h" +#include "api/audio_codecs/audio_decoder_factory_template.h" +#include "api/audio_codecs/audio_encoder_factory_template.h" +#include "api/audio_codecs/ilbc/audio_decoder_ilbc.h" +#include "api/audio_codecs/ilbc/audio_encoder_ilbc.h" +#include "api/audio_codecs/isac/audio_decoder_isac_float.h" +#include "api/audio_codecs/isac/audio_encoder_isac_float.h" +#include "api/audio_codecs/opus/audio_decoder_opus.h" +#include "api/audio_codecs/opus/audio_encoder_opus.h" +#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h" +#include "modules/audio_coding/test/PCMFile.h" +#include "rtc_base/strings/string_builder.h" +#include "test/gtest.h" +#include "test/testsupport/file_utils.h" + +namespace webrtc { + +MonitoringAudioPacketizationCallback::MonitoringAudioPacketizationCallback( + AudioPacketizationCallback* next) + : next_(next) { + ResetStatistics(); +} + +int32_t MonitoringAudioPacketizationCallback::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) { + counter_[static_cast<int>(frame_type)]++; + return next_->SendData(frame_type, payload_type, timestamp, payload_data, + payload_len_bytes, absolute_capture_timestamp_ms); +} + +void MonitoringAudioPacketizationCallback::PrintStatistics() { + printf("\n"); + printf("kEmptyFrame %u\n", + counter_[static_cast<int>(AudioFrameType::kEmptyFrame)]); + printf("kAudioFrameSpeech %u\n", + counter_[static_cast<int>(AudioFrameType::kAudioFrameSpeech)]); + printf("kAudioFrameCN %u\n", + counter_[static_cast<int>(AudioFrameType::kAudioFrameCN)]); + printf("\n\n"); +} + +void MonitoringAudioPacketizationCallback::ResetStatistics() { + memset(counter_, 0, sizeof(counter_)); +} + +void MonitoringAudioPacketizationCallback::GetStatistics(uint32_t* counter) { + memcpy(counter, counter_, sizeof(counter_)); +} + +TestVadDtx::TestVadDtx() + : encoder_factory_(CreateAudioEncoderFactory<AudioEncoderIlbc, + AudioEncoderIsacFloat, + AudioEncoderOpus>()), + decoder_factory_(CreateAudioDecoderFactory<AudioDecoderIlbc, + AudioDecoderIsacFloat, + AudioDecoderOpus>()), + acm_send_(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + acm_receive_(AudioCodingModule::Create( + AudioCodingModule::Config(decoder_factory_))), + channel_(std::make_unique<Channel>()), + packetization_callback_( + std::make_unique<MonitoringAudioPacketizationCallback>( + channel_.get())) { + EXPECT_EQ( + 0, acm_send_->RegisterTransportCallback(packetization_callback_.get())); + channel_->RegisterReceiverACM(acm_receive_.get()); +} + +bool TestVadDtx::RegisterCodec(const SdpAudioFormat& codec_format, + absl::optional<Vad::Aggressiveness> vad_mode) { + constexpr int payload_type = 17, cn_payload_type = 117; + bool added_comfort_noise = false; + + auto encoder = encoder_factory_->MakeAudioEncoder(payload_type, codec_format, + absl::nullopt); + if (vad_mode.has_value() && + !absl::EqualsIgnoreCase(codec_format.name, "opus")) { + AudioEncoderCngConfig config; + config.speech_encoder = std::move(encoder); + config.num_channels = 1; + config.payload_type = cn_payload_type; + config.vad_mode = vad_mode.value(); + encoder = CreateComfortNoiseEncoder(std::move(config)); + added_comfort_noise = true; + } + channel_->SetIsStereo(encoder->NumChannels() > 1); + acm_send_->SetEncoder(std::move(encoder)); + + std::map<int, SdpAudioFormat> receive_codecs = {{payload_type, codec_format}}; + acm_receive_->SetReceiveCodecs(receive_codecs); + + return added_comfort_noise; +} + +// Encoding a file and see if the numbers that various packets occur follow +// the expectation. +void TestVadDtx::Run(absl::string_view in_filename, + int frequency, + int channels, + absl::string_view out_filename, + bool append, + const int* expects) { + packetization_callback_->ResetStatistics(); + + PCMFile in_file; + in_file.Open(in_filename, frequency, "rb"); + in_file.ReadStereo(channels > 1); + // Set test length to 1000 ms (100 blocks of 10 ms each). + in_file.SetNum10MsBlocksToRead(100); + // Fast-forward both files 500 ms (50 blocks). The first second of the file is + // silence, but we want to keep half of that to test silence periods. + in_file.FastForward(50); + + PCMFile out_file; + if (append) { + out_file.Open(out_filename, kOutputFreqHz, "ab"); + } else { + out_file.Open(out_filename, kOutputFreqHz, "wb"); + } + + uint16_t frame_size_samples = in_file.PayloadLength10Ms(); + AudioFrame audio_frame; + while (!in_file.EndOfFile()) { + in_file.Read10MsData(audio_frame); + audio_frame.timestamp_ = time_stamp_; + time_stamp_ += frame_size_samples; + EXPECT_GE(acm_send_->Add10MsData(audio_frame), 0); + bool muted; + acm_receive_->PlayoutData10Ms(kOutputFreqHz, &audio_frame, &muted); + ASSERT_FALSE(muted); + out_file.Write10MsData(audio_frame); + } + + in_file.Close(); + out_file.Close(); + +#ifdef PRINT_STAT + packetization_callback_->PrintStatistics(); +#endif + + uint32_t stats[3]; + packetization_callback_->GetStatistics(stats); + packetization_callback_->ResetStatistics(); + + for (const auto& st : stats) { + int i = &st - stats; // Calculate the current position in stats. + switch (expects[i]) { + case 0: { + EXPECT_EQ(0u, st) << "stats[" << i << "] error."; + break; + } + case 1: { + EXPECT_GT(st, 0u) << "stats[" << i << "] error."; + break; + } + } + } +} + +// Following is the implementation of TestWebRtcVadDtx. +TestWebRtcVadDtx::TestWebRtcVadDtx() : output_file_num_(0) {} + +void TestWebRtcVadDtx::Perform() { + RunTestCases({"ISAC", 16000, 1}); + RunTestCases({"ISAC", 32000, 1}); + RunTestCases({"ILBC", 8000, 1}); + RunTestCases({"opus", 48000, 2}); +} + +// Test various configurations on VAD/DTX. +void TestWebRtcVadDtx::RunTestCases(const SdpAudioFormat& codec_format) { + Test(/*new_outfile=*/true, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, absl::nullopt)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadAggressive)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadLowBitrate)); + + Test(/*new_outfile=*/false, /*expect_dtx_enabled=*/RegisterCodec( + codec_format, Vad::kVadVeryAggressive)); + + Test(/*new_outfile=*/false, + /*expect_dtx_enabled=*/RegisterCodec(codec_format, Vad::kVadNormal)); +} + +// Set the expectation and run the test. +void TestWebRtcVadDtx::Test(bool new_outfile, bool expect_dtx_enabled) { + int expects[] = {-1, 1, expect_dtx_enabled, 0, 0}; + if (new_outfile) { + output_file_num_++; + } + rtc::StringBuilder out_filename; + out_filename << webrtc::test::OutputPath() << "testWebRtcVadDtx_outFile_" + << output_file_num_ << ".pcm"; + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename.str(), !new_outfile, expects); +} + +// Following is the implementation of TestOpusDtx. +void TestOpusDtx::Perform() { + int expects[] = {0, 1, 0, 0, 0}; + + // Register Opus as send codec + std::string out_filename = + webrtc::test::OutputPath() + "testOpusDtx_outFile_mono.pcm"; + RegisterCodec({"opus", 48000, 2}, absl::nullopt); + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(false); + }); + + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename, false, expects); + + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(true); + }); + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1; + Run(webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm"), 32000, 1, + out_filename, true, expects); + + // Register stereo Opus as send codec + out_filename = webrtc::test::OutputPath() + "testOpusDtx_outFile_stereo.pcm"; + RegisterCodec({"opus", 48000, 2, {{"stereo", "1"}}}, absl::nullopt); + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(false); + }); + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 0; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 0; + Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000, + 2, out_filename, false, expects); + + acm_send_->ModifyEncoder([](std::unique_ptr<AudioEncoder>* encoder_ptr) { + (*encoder_ptr)->SetDtx(true); + // The default bitrate will not generate frames recognized as CN on desktop + // since the frames will be encoded as CELT. Set a low target bitrate to get + // consistent behaviour across platforms. + (*encoder_ptr)->OnReceivedTargetAudioBitrate(24000); + }); + + expects[static_cast<int>(AudioFrameType::kEmptyFrame)] = 1; + expects[static_cast<int>(AudioFrameType::kAudioFrameCN)] = 1; + Run(webrtc::test::ResourcePath("audio_coding/teststereo32kHz", "pcm"), 32000, + 2, out_filename, true, expects); +} + +} // namespace webrtc |