diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn index df74eff24d..60a3765aa7 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -101,6 +101,8 @@ rtc_static_library("rtc_media") { libs = [] deps = [] sources = [ + "engine/apm_helpers.cc", + "engine/apm_helpers.h", "engine/internaldecoderfactory.cc", "engine/internaldecoderfactory.h", "engine/internalencoderfactory.cc", @@ -319,6 +321,7 @@ if (rtc_include_tests) { "base/videocapturer_unittest.cc", "base/videocommon_unittest.cc", "base/videoengine_unittest.h", + "engine/apm_helpers_unittest.cc", "engine/internaldecoderfactory_unittest.cc", "engine/nullwebrtcvideoengine_unittest.cc", "engine/payload_type_mapper_unittest.cc", diff --git a/webrtc/media/engine/apm_helpers.cc b/webrtc/media/engine/apm_helpers.cc new file mode 100644 index 0000000000..a806523d62 --- /dev/null +++ b/webrtc/media/engine/apm_helpers.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 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 "webrtc/media/engine/apm_helpers.h" + +#include "webrtc/base/logging.h" +#include "webrtc/modules/audio_device/include/audio_device.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/voice_engine/transmit_mixer.h" + +namespace webrtc { +namespace apm_helpers { + +AgcConfig GetAgcConfig(AudioProcessing* apm) { + RTC_DCHECK(apm); + AgcConfig result; + result.targetLeveldBOv = apm->gain_control()->target_level_dbfs(); + result.digitalCompressionGaindB = apm->gain_control()->compression_gain_db(); + result.limiterEnable = apm->gain_control()->is_limiter_enabled(); + return result; +} + +void SetAgcConfig(AudioProcessing* apm, + const AgcConfig& config) { + RTC_DCHECK(apm); + GainControl* gc = apm->gain_control(); + if (gc->set_target_level_dbfs(config.targetLeveldBOv) != 0) { + LOG(LS_ERROR) << "Failed to set target level: " << config.targetLeveldBOv; + } + if (gc->set_compression_gain_db(config.digitalCompressionGaindB) != 0) { + LOG(LS_ERROR) << "Failed to set compression gain: " + << config.digitalCompressionGaindB; + } + if (gc->enable_limiter(config.limiterEnable) != 0) { + LOG(LS_ERROR) << "Failed to set limiter on/off: " << config.limiterEnable; + } +} + +void SetAgcStatus(AudioProcessing* apm, + AudioDeviceModule* adm, + bool enable, + AgcModes mode) { + RTC_DCHECK(apm); + RTC_DCHECK(adm); +#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) + RTC_DCHECK_EQ(kAgcFixedDigital, mode); + GainControl::Mode agc_mode = GainControl::kFixedDigital; +#else + RTC_DCHECK_EQ(kAgcAdaptiveAnalog, mode); + GainControl::Mode agc_mode = GainControl::kAdaptiveAnalog; +#endif + GainControl* gc = apm->gain_control(); + if (gc->set_mode(agc_mode) != 0) { + LOG(LS_ERROR) << "Failed to set AGC mode: " << agc_mode; + return; + } + if (gc->Enable(enable) != 0) { + LOG(LS_ERROR) << "Failed to enable/disable AGC: " << enable; + return; + } + // Set AGC state in the ADM when adaptive AGC mode has been selected. + if (adm->SetAGC(enable && agc_mode == GainControl::kAdaptiveAnalog) != 0) { + LOG(LS_ERROR) << "Failed to set AGC mode in ADM: " << enable; + return; + } + LOG(LS_INFO) << "AGC set to " << enable << " with mode " << mode; +} + +void SetEcStatus(AudioProcessing* apm, + bool enable, + EcModes mode) { + RTC_DCHECK(apm); + RTC_DCHECK(mode == kEcConference || mode == kEcAecm) << "mode: " << mode; + EchoCancellation* ec = apm->echo_cancellation(); + EchoControlMobile* ecm = apm->echo_control_mobile(); + if (mode == kEcConference) { + // Disable the AECM before enabling the AEC. + if (enable && ecm->is_enabled() && ecm->Enable(false) != 0) { + LOG(LS_ERROR) << "Failed to disable AECM."; + return; + } + if (ec->Enable(enable) != 0) { + LOG(LS_ERROR) << "Failed to enable/disable AEC: " << enable; + return; + } + if (ec->set_suppression_level(EchoCancellation::kHighSuppression) + != 0) { + LOG(LS_ERROR) << "Failed to set high AEC aggressiveness."; + return; + } + } else { + // Disable the AEC before enabling the AECM. + if (enable && ec->is_enabled() && ec->Enable(false) != 0) { + LOG(LS_ERROR) << "Failed to disable AEC."; + return; + } + if (ecm->Enable(enable) != 0) { + LOG(LS_ERROR) << "Failed to enable/disable AECM: " << enable; + return; + } + } + LOG(LS_INFO) << "Echo control set to " << enable << " with mode " << mode; +} + +void SetEcMetricsStatus(AudioProcessing* apm, bool enable) { + RTC_DCHECK(apm); + if ((apm->echo_cancellation()->enable_metrics(enable) != 0) || + (apm->echo_cancellation()->enable_delay_logging(enable) != 0)) { + LOG(LS_ERROR) << "Failed to enable/disable EC metrics: " << enable; + return; + } + LOG(LS_INFO) << "EC metrics set to " << enable; +} + +void SetAecmMode(AudioProcessing* apm, bool enable) { + RTC_DCHECK(apm); + EchoControlMobile* ecm = apm->echo_control_mobile(); + RTC_DCHECK_EQ(EchoControlMobile::kSpeakerphone, ecm->routing_mode()); + if (ecm->enable_comfort_noise(enable) != 0) { + LOG(LS_ERROR) << "Failed to enable/disable CNG: " << enable; + return; + } + LOG(LS_INFO) << "CNG set to " << enable; +} + +void SetNsStatus(AudioProcessing* apm, bool enable) { + RTC_DCHECK(apm); + NoiseSuppression* ns = apm->noise_suppression(); + if (ns->set_level(NoiseSuppression::kHigh) != 0) { + LOG(LS_ERROR) << "Failed to set high NS level."; + return; + } + if (ns->Enable(enable) != 0) { + LOG(LS_ERROR) << "Failed to enable/disable NS: " << enable; + return; + } + LOG(LS_INFO) << "NS set to " << enable; +} + +void SetTypingDetectionStatus(AudioProcessing* apm, bool enable) { + RTC_DCHECK(apm); +#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION + // Typing detection takes place in TransmitMixer::PrepareDemux() and + // TransmitMixer::TypingDetection(). The typing detection algorithm takes as + // input two booleans: + // 1. A signal whether a key was pressed during the audio frame. + // 2. Whether VAD is active or not. + // TransmitMixer will not even call the detector if APM has set kVadUnknown in + // the audio frame after near end processing, so enabling/disabling VAD is + // sufficient for turning typing detection on/off. + // TODO(solenberg): Rather than relying on a side effect, consider forcing the + // feature on/off in TransmitMixer. + VoiceDetection* vd = apm->voice_detection(); + if (vd->Enable(enable)) { + LOG(LS_ERROR) << "Failed to enable/disable VAD: " << enable; + return; + } + if (vd->set_likelihood(VoiceDetection::kVeryLowLikelihood)) { + LOG(LS_ERROR) << "Failed to set low VAD likelihood."; + return; + } + LOG(LS_INFO) << "VAD set to " << enable << " for typing detection."; +#endif +} +} // namespace apm_helpers +} // namespace webrtc diff --git a/webrtc/media/engine/apm_helpers.h b/webrtc/media/engine/apm_helpers.h new file mode 100644 index 0000000000..1bcf0511ec --- /dev/null +++ b/webrtc/media/engine/apm_helpers.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef WEBRTC_MEDIA_ENGINE_APM_HELPERS_H_ +#define WEBRTC_MEDIA_ENGINE_APM_HELPERS_H_ + +#include "webrtc/common_types.h" + +namespace webrtc { + +class AudioProcessing; +class AudioDeviceModule; + +namespace apm_helpers { + +AgcConfig GetAgcConfig(AudioProcessing* apm); +void SetAgcConfig(AudioProcessing* apm, + const AgcConfig& config); +void SetAgcStatus(AudioProcessing* apm, + AudioDeviceModule* adm, + bool enable, + AgcModes mode); +void SetEcStatus(AudioProcessing* apm, + bool enable, + EcModes mode); +void SetEcMetricsStatus(AudioProcessing* apm, bool enable); +void SetAecmMode(AudioProcessing* apm, bool enable_cng); +void SetNsStatus(AudioProcessing* apm, bool enable); +void SetTypingDetectionStatus(AudioProcessing* apm, bool enable); + +} // namespace apm_helpers +} // namespace webrtc + +#endif // WEBRTC_MEDIA_ENGINE_APM_HELPERS_H_ diff --git a/webrtc/media/engine/apm_helpers_unittest.cc b/webrtc/media/engine/apm_helpers_unittest.cc new file mode 100644 index 0000000000..80b03ad0e1 --- /dev/null +++ b/webrtc/media/engine/apm_helpers_unittest.cc @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2017 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 "webrtc/media/engine/apm_helpers.h" + +#include "webrtc/media/engine/webrtcvoe.h" +#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_decoder_factory.h" +#include "webrtc/modules/audio_device/include/mock_audio_device.h" +#include "webrtc/modules/audio_processing/include/audio_processing.h" +#include "webrtc/test/gmock.h" +#include "webrtc/test/gtest.h" +#include "webrtc/voice_engine/transmit_mixer.h" + +namespace webrtc { +namespace { + +constexpr AgcConfig kDefaultAgcConfig = { 3, 9, true }; + +struct TestHelper { + TestHelper() { + // This replicates the conditions from voe_auto_test. + Config config; + config.Set(new ExperimentalAgc(false)); + EXPECT_EQ(0, voe_wrapper_.base()->Init( + &mock_audio_device_, + AudioProcessing::Create(config), + MockAudioDecoderFactory::CreateEmptyFactory())); + } + + AudioProcessing* apm() { + return voe_wrapper_.base()->audio_processing(); + } + + const AudioProcessing* apm() const { + return voe_wrapper_.base()->audio_processing(); + } + + test::MockAudioDeviceModule* adm() { + return &mock_audio_device_; + } + + voe::TransmitMixer* transmit_mixer() { + return voe_wrapper_.base()->transmit_mixer(); + } + + bool GetEcMetricsStatus() const { + EchoCancellation* ec = apm()->echo_cancellation(); + bool metrics_enabled = ec->are_metrics_enabled(); + EXPECT_EQ(metrics_enabled, ec->is_delay_logging_enabled()); + return metrics_enabled; + } + + bool CanGetEcMetrics() const { + EchoCancellation* ec = apm()->echo_cancellation(); + EchoCancellation::Metrics metrics; + int metrics_result = ec->GetMetrics(&metrics); + int median = 0; + int std = 0; + float fraction = 0; + int delay_metrics_result = ec->GetDelayMetrics(&median, &std, &fraction); + return metrics_result == AudioProcessing::kNoError && + delay_metrics_result == AudioProcessing::kNoError; + } + + private: + testing::NiceMock mock_audio_device_; + cricket::VoEWrapper voe_wrapper_; +}; +} // namespace + +TEST(ApmHelpersTest, AgcConfig_DefaultConfiguration) { + TestHelper helper; + AgcConfig agc_config = + apm_helpers::GetAgcConfig(helper.apm()); + + EXPECT_EQ(kDefaultAgcConfig.targetLeveldBOv, agc_config.targetLeveldBOv); + EXPECT_EQ(kDefaultAgcConfig.digitalCompressionGaindB, + agc_config.digitalCompressionGaindB); + EXPECT_EQ(kDefaultAgcConfig.limiterEnable, agc_config.limiterEnable); +} + +TEST(ApmHelpersTest, AgcConfig_GetAndSet) { + const AgcConfig agc_config = { 11, 17, false }; + + TestHelper helper; + apm_helpers::SetAgcConfig(helper.apm(), agc_config); + AgcConfig actual_config = + apm_helpers::GetAgcConfig(helper.apm()); + + EXPECT_EQ(agc_config.digitalCompressionGaindB, + actual_config.digitalCompressionGaindB); + EXPECT_EQ(agc_config.limiterEnable, + actual_config.limiterEnable); + EXPECT_EQ(agc_config.targetLeveldBOv, + actual_config.targetLeveldBOv); +} + +TEST(ApmHelpersTest, AgcStatus_DefaultMode) { + TestHelper helper; + GainControl* gc = helper.apm()->gain_control(); +#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) + EXPECT_FALSE(gc->is_enabled()); + EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); +#else + EXPECT_TRUE(gc->is_enabled()); + EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); +#endif +} + +TEST(ApmHelpersTest, AgcStatus_EnableDisable) { + TestHelper helper; + GainControl* gc = helper.apm()->gain_control(); +#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) + apm_helpers::SetAgcStatus(helper.apm(), helper.adm(), false, + kAgcFixedDigital); + EXPECT_FALSE(gc->is_enabled()); + EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); + + apm_helpers::SetAgcStatus(helper.apm(), helper.adm(), true, + kAgcFixedDigital); + EXPECT_TRUE(gc->is_enabled()); + EXPECT_EQ(GainControl::kFixedDigital, gc->mode()); +#else + EXPECT_CALL(*helper.adm(), SetAGC(false)).WillOnce(testing::Return(0)); + apm_helpers::SetAgcStatus(helper.apm(), helper.adm(), false, + kAgcAdaptiveAnalog); + EXPECT_FALSE(gc->is_enabled()); + EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); + + EXPECT_CALL(*helper.adm(), SetAGC(true)).WillOnce(testing::Return(0)); + apm_helpers::SetAgcStatus(helper.apm(), helper.adm(), true, + kAgcAdaptiveAnalog); + EXPECT_TRUE(gc->is_enabled()); + EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode()); +#endif +} + +TEST(ApmHelpersTest, EcStatus_DefaultMode) { + TestHelper helper; + EchoCancellation* ec = helper.apm()->echo_cancellation(); + EchoControlMobile* ecm = helper.apm()->echo_control_mobile(); + EXPECT_FALSE(ec->is_enabled()); + EXPECT_FALSE(ecm->is_enabled()); +} + +TEST(ApmHelpersTest, EcStatus_EnableDisable) { + TestHelper helper; + EchoCancellation* ec = helper.apm()->echo_cancellation(); + EchoControlMobile* ecm = helper.apm()->echo_control_mobile(); + + apm_helpers::SetEcStatus(helper.apm(), true, kEcAecm); + EXPECT_FALSE(ec->is_enabled()); + EXPECT_TRUE(ecm->is_enabled()); + + apm_helpers::SetEcStatus(helper.apm(), false, kEcAecm); + EXPECT_FALSE(ec->is_enabled()); + EXPECT_FALSE(ecm->is_enabled()); + + apm_helpers::SetEcStatus(helper.apm(), true, kEcConference); + EXPECT_TRUE(ec->is_enabled()); + EXPECT_FALSE(ecm->is_enabled()); + EXPECT_EQ(EchoCancellation::kHighSuppression, ec->suppression_level()); + + apm_helpers::SetEcStatus(helper.apm(), false, kEcConference); + EXPECT_FALSE(ec->is_enabled()); + EXPECT_FALSE(ecm->is_enabled()); + EXPECT_EQ(EchoCancellation::kHighSuppression, ec->suppression_level()); + + apm_helpers::SetEcStatus(helper.apm(), true, kEcAecm); + EXPECT_FALSE(ec->is_enabled()); + EXPECT_TRUE(ecm->is_enabled()); +} + +TEST(ApmHelpersTest, EcMetrics_DefaultMode) { + TestHelper helper; + apm_helpers::SetEcStatus(helper.apm(), true, kEcConference); + EXPECT_TRUE(helper.GetEcMetricsStatus()); +} + +TEST(ApmHelpersTest, EcMetrics_CanEnableDisable) { + TestHelper helper; + apm_helpers::SetEcStatus(helper.apm(), true, kEcConference); + + apm_helpers::SetEcMetricsStatus(helper.apm(), true); + EXPECT_TRUE(helper.GetEcMetricsStatus()); + apm_helpers::SetEcMetricsStatus(helper.apm(), false); + EXPECT_FALSE(helper.GetEcMetricsStatus()); +} + +TEST(ApmHelpersTest, EcMetrics_NoStatsUnlessEcMetricsAndEcEnabled) { + TestHelper helper; + EXPECT_FALSE(helper.CanGetEcMetrics()); + + apm_helpers::SetEcMetricsStatus(helper.apm(), true); + EXPECT_FALSE(helper.CanGetEcMetrics()); + + apm_helpers::SetEcStatus(helper.apm(), true, kEcConference); + EXPECT_TRUE(helper.CanGetEcMetrics()); + + apm_helpers::SetEcMetricsStatus(helper.apm(), false); + EXPECT_FALSE(helper.CanGetEcMetrics()); +} + +TEST(ApmHelpersTest, AecmMode_DefaultMode) { + TestHelper helper; + EchoControlMobile* ecm = helper.apm()->echo_control_mobile(); + EXPECT_EQ(EchoControlMobile::kSpeakerphone, ecm->routing_mode()); + EXPECT_TRUE(ecm->is_comfort_noise_enabled()); +} + +TEST(ApmHelpersTest, AecmMode_EnableDisableCng) { + TestHelper helper; + EchoControlMobile* ecm = helper.apm()->echo_control_mobile(); + apm_helpers::SetAecmMode(helper.apm(), false); + EXPECT_FALSE(ecm->is_comfort_noise_enabled()); + apm_helpers::SetAecmMode(helper.apm(), true); + EXPECT_TRUE(ecm->is_comfort_noise_enabled()); +} + +TEST(ApmHelpersTest, NsStatus_DefaultMode) { + TestHelper helper; + NoiseSuppression* ns = helper.apm()->noise_suppression(); + EXPECT_EQ(NoiseSuppression::kModerate, ns->level()); + EXPECT_FALSE(ns->is_enabled()); +} + +TEST(ApmHelpersTest, NsStatus_EnableDisable) { + TestHelper helper; + NoiseSuppression* ns = helper.apm()->noise_suppression(); + apm_helpers::SetNsStatus(helper.apm(), true); + EXPECT_EQ(NoiseSuppression::kHigh, ns->level()); + EXPECT_TRUE(ns->is_enabled()); + apm_helpers::SetNsStatus(helper.apm(), false); + EXPECT_EQ(NoiseSuppression::kHigh, ns->level()); + EXPECT_FALSE(ns->is_enabled()); +} + +TEST(ApmHelpersTest, TypingDetectionStatus_DefaultMode) { + TestHelper helper; + VoiceDetection* vd = helper.apm()->voice_detection(); + EXPECT_FALSE(vd->is_enabled()); +} + +TEST(ApmHelpersTest, TypingDetectionStatus_EnableDisable) { + TestHelper helper; + VoiceDetection* vd = helper.apm()->voice_detection(); + apm_helpers::SetTypingDetectionStatus(helper.apm(), true); + EXPECT_TRUE(vd->is_enabled()); + apm_helpers::SetTypingDetectionStatus(helper.apm(), false); + EXPECT_FALSE(vd->is_enabled()); +} + +// TODO(solenberg): Move this test to a better place - added here for the sake +// of duplicating all relevant tests from audio_processing_test.cc. +TEST(ApmHelpersTest, HighPassFilter_DefaultMode) { + TestHelper helper; + EXPECT_TRUE(helper.apm()->high_pass_filter()->is_enabled()); +} + +// TODO(solenberg): Move this test to a better place - added here for the sake +// of duplicating all relevant tests from audio_processing_test.cc. +TEST(ApmHelpersTest, StereoSwapping_DefaultMode) { + TestHelper helper; + EXPECT_FALSE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled()); +} + +// TODO(solenberg): Move this test to a better place - added here for the sake +// of duplicating all relevant tests from audio_processing_test.cc. +TEST(ApmHelpersTest, StereoSwapping_EnableDisable) { + TestHelper helper; + helper.transmit_mixer()->EnableStereoChannelSwapping(true); + EXPECT_TRUE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled()); + helper.transmit_mixer()->EnableStereoChannelSwapping(false); + EXPECT_FALSE(helper.transmit_mixer()->IsStereoChannelSwappingEnabled()); +} +} // namespace webrtc diff --git a/webrtc/media/engine/fakewebrtcvoiceengine.h b/webrtc/media/engine/fakewebrtcvoiceengine.h index 3b992f1e2c..18c77663a2 100644 --- a/webrtc/media/engine/fakewebrtcvoiceengine.h +++ b/webrtc/media/engine/fakewebrtcvoiceengine.h @@ -27,6 +27,12 @@ #include "webrtc/modules/audio_coding/acm2/rent_a_codec.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" +namespace webrtc { +namespace voe { +class TransmitMixer; +} // namespace voe +} // namespace webrtc + namespace cricket { static const int kOpusBandwidthNb = 4000; @@ -53,8 +59,7 @@ static const int kOpusBandwidthFb = 20000; #define WEBRTC_FUNC(method, args) int method args override class FakeWebRtcVoiceEngine - : public webrtc::VoEAudioProcessing, - public webrtc::VoEBase, public webrtc::VoECodec, + : public webrtc::VoEBase, public webrtc::VoECodec, public webrtc::VoEHardware, public webrtc::VoEVolumeControl { public: @@ -64,15 +69,14 @@ class FakeWebRtcVoiceEngine bool neteq_fast_accelerate = false; }; - explicit FakeWebRtcVoiceEngine(webrtc::AudioProcessing* apm) : apm_(apm) { - memset(&agc_config_, 0, sizeof(agc_config_)); + explicit FakeWebRtcVoiceEngine(webrtc::AudioProcessing* apm, + webrtc::voe::TransmitMixer* transmit_mixer) + : apm_(apm), transmit_mixer_(transmit_mixer) { } ~FakeWebRtcVoiceEngine() override { RTC_CHECK(channels_.empty()); } - bool ec_metrics_enabled() const { return ec_metrics_enabled_; } - bool IsInited() const { return inited_; } int GetLastChannel() const { return last_channel_; } int GetNumChannels() const { return static_cast(channels_.size()); } @@ -104,6 +108,9 @@ class FakeWebRtcVoiceEngine webrtc::AudioDeviceModule* audio_device_module() override { return nullptr; } + webrtc::voe::TransmitMixer* transmit_mixer() override { + return transmit_mixer_; + } WEBRTC_FUNC(CreateChannel, ()) { return CreateChannel(webrtc::VoEBase::ChannelConfig()); } @@ -233,98 +240,6 @@ class FakeWebRtcVoiceEngine WEBRTC_STUB(SetOutputVolumePan, (int channel, float left, float right)); WEBRTC_STUB(GetOutputVolumePan, (int channel, float& left, float& right)); - // webrtc::VoEAudioProcessing - WEBRTC_FUNC(SetNsStatus, (bool enable, webrtc::NsModes mode)) { - ns_enabled_ = enable; - ns_mode_ = mode; - return 0; - } - WEBRTC_FUNC(GetNsStatus, (bool& enabled, webrtc::NsModes& mode)) { - enabled = ns_enabled_; - mode = ns_mode_; - return 0; - } - WEBRTC_FUNC(SetAgcStatus, (bool enable, webrtc::AgcModes mode)) { - agc_enabled_ = enable; - agc_mode_ = mode; - return 0; - } - WEBRTC_FUNC(GetAgcStatus, (bool& enabled, webrtc::AgcModes& mode)) { - enabled = agc_enabled_; - mode = agc_mode_; - return 0; - } - WEBRTC_FUNC(SetAgcConfig, (webrtc::AgcConfig config)) { - agc_config_ = config; - return 0; - } - WEBRTC_FUNC(GetAgcConfig, (webrtc::AgcConfig& config)) { - config = agc_config_; - return 0; - } - WEBRTC_FUNC(SetEcStatus, (bool enable, webrtc::EcModes mode)) { - ec_enabled_ = enable; - ec_mode_ = mode; - return 0; - } - WEBRTC_FUNC(GetEcStatus, (bool& enabled, webrtc::EcModes& mode)) { - enabled = ec_enabled_; - mode = ec_mode_; - return 0; - } - WEBRTC_STUB(EnableDriftCompensation, (bool enable)) - WEBRTC_BOOL_STUB(DriftCompensationEnabled, ()) - WEBRTC_VOID_STUB(SetDelayOffsetMs, (int offset)) - WEBRTC_STUB(DelayOffsetMs, ()); - WEBRTC_FUNC(SetAecmMode, (webrtc::AecmModes mode, bool enableCNG)) { - aecm_mode_ = mode; - cng_enabled_ = enableCNG; - return 0; - } - WEBRTC_FUNC(GetAecmMode, (webrtc::AecmModes& mode, bool& enabledCNG)) { - mode = aecm_mode_; - enabledCNG = cng_enabled_; - return 0; - } - WEBRTC_STUB(VoiceActivityIndicator, (int channel)); - WEBRTC_FUNC(SetEcMetricsStatus, (bool enable)) { - ec_metrics_enabled_ = enable; - return 0; - } - WEBRTC_STUB(GetEcMetricsStatus, (bool& enabled)); - WEBRTC_STUB(GetEchoMetrics, (int& ERL, int& ERLE, int& RERL, int& A_NLP)); - WEBRTC_STUB(GetEcDelayMetrics, (int& delay_median, int& delay_std, - float& fraction_poor_delays)); - WEBRTC_STUB(StartDebugRecording, (const char* fileNameUTF8)); - WEBRTC_STUB(StartDebugRecording, (FILE* handle)); - WEBRTC_STUB(StopDebugRecording, ()); - WEBRTC_FUNC(SetTypingDetectionStatus, (bool enable)) { - typing_detection_enabled_ = enable; - return 0; - } - WEBRTC_FUNC(GetTypingDetectionStatus, (bool& enabled)) { - enabled = typing_detection_enabled_; - return 0; - } - WEBRTC_STUB(TimeSinceLastTyping, (int& seconds)); - WEBRTC_STUB(SetTypingDetectionParameters, (int timeWindow, - int costPerTyping, - int reportingThreshold, - int penaltyDecay, - int typeEventDelay)); - int EnableHighPassFilter(bool enable) override { - highpass_filter_enabled_ = enable; - return 0; - } - bool IsHighPassFilterEnabled() override { - return highpass_filter_enabled_; - } - bool IsStereoChannelSwappingEnabled() override { - return stereo_swapping_enabled_; - } - void EnableStereoChannelSwapping(bool enable) override { - stereo_swapping_enabled_ = enable; - } size_t GetNetEqCapacity() const { auto ch = channels_.find(last_channel_); RTC_DCHECK(ch != channels_.end()); @@ -341,20 +256,8 @@ class FakeWebRtcVoiceEngine int last_channel_ = -1; std::map channels_; bool fail_create_channel_ = false; - bool ec_enabled_ = false; - bool ec_metrics_enabled_ = false; - bool cng_enabled_ = false; - bool ns_enabled_ = false; - bool agc_enabled_ = false; - bool highpass_filter_enabled_ = false; - bool stereo_swapping_enabled_ = false; - bool typing_detection_enabled_ = false; - webrtc::EcModes ec_mode_ = webrtc::kEcDefault; - webrtc::AecmModes aecm_mode_ = webrtc::kAecmSpeakerphone; - webrtc::NsModes ns_mode_ = webrtc::kNsDefault; - webrtc::AgcModes agc_mode_ = webrtc::kAgcDefault; - webrtc::AgcConfig agc_config_; webrtc::AudioProcessing* apm_ = nullptr; + webrtc::voe::TransmitMixer* transmit_mixer_ = nullptr; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(FakeWebRtcVoiceEngine); }; diff --git a/webrtc/media/engine/webrtcvoe.h b/webrtc/media/engine/webrtcvoe.h index 6884aa4ea3..eabde3659b 100644 --- a/webrtc/media/engine/webrtcvoe.h +++ b/webrtc/media/engine/webrtcvoe.h @@ -17,7 +17,6 @@ #include "webrtc/common_types.h" #include "webrtc/modules/audio_device/include/audio_device.h" -#include "webrtc/voice_engine/include/voe_audio_processing.h" #include "webrtc/voice_engine/include/voe_base.h" #include "webrtc/voice_engine/include/voe_codec.h" #include "webrtc/voice_engine/include/voe_errors.h" @@ -78,17 +77,15 @@ class scoped_voe_ptr { class VoEWrapper { public: VoEWrapper() - : engine_(webrtc::VoiceEngine::Create()), processing_(engine_), + : engine_(webrtc::VoiceEngine::Create()), base_(engine_), codec_(engine_), hw_(engine_), volume_(engine_) { } - VoEWrapper(webrtc::VoEAudioProcessing* processing, - webrtc::VoEBase* base, + VoEWrapper(webrtc::VoEBase* base, webrtc::VoECodec* codec, webrtc::VoEHardware* hw, webrtc::VoEVolumeControl* volume) : engine_(NULL), - processing_(processing), base_(base), codec_(codec), hw_(hw), @@ -96,7 +93,6 @@ class VoEWrapper { } ~VoEWrapper() {} webrtc::VoiceEngine* engine() const { return engine_.get(); } - webrtc::VoEAudioProcessing* processing() const { return processing_.get(); } webrtc::VoEBase* base() const { return base_.get(); } webrtc::VoECodec* codec() const { return codec_.get(); } webrtc::VoEHardware* hw() const { return hw_.get(); } @@ -105,7 +101,6 @@ class VoEWrapper { private: scoped_voe_engine engine_; - scoped_voe_ptr processing_; scoped_voe_ptr base_; scoped_voe_ptr codec_; scoped_voe_ptr hw_; diff --git a/webrtc/media/engine/webrtcvoiceengine.cc b/webrtc/media/engine/webrtcvoiceengine.cc index f374b76e3e..e0d9884e8f 100644 --- a/webrtc/media/engine/webrtcvoiceengine.cc +++ b/webrtc/media/engine/webrtcvoiceengine.cc @@ -32,6 +32,7 @@ #include "webrtc/media/base/audiosource.h" #include "webrtc/media/base/mediaconstants.h" #include "webrtc/media/base/streamparams.h" +#include "webrtc/media/engine/apm_helpers.h" #include "webrtc/media/engine/payload_type_mapper.h" #include "webrtc/media/engine/webrtcmediaengine.h" #include "webrtc/media/engine/webrtcvoe.h" @@ -40,6 +41,7 @@ #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/system_wrappers/include/field_trial.h" #include "webrtc/system_wrappers/include/trace.h" +#include "webrtc/voice_engine/transmit_mixer.h" namespace cricket { namespace { @@ -559,7 +561,7 @@ rtc::Optional ComputeSendBitrate(int max_send_bitrate_bps, return rtc::Optional(codec_rate); } -} // namespace { +} // namespace bool WebRtcVoiceEngine::ToCodecInst(const AudioCodec& in, webrtc::CodecInst* out) { @@ -620,10 +622,12 @@ WebRtcVoiceEngine::WebRtcVoiceEngine( apm_ = voe_wrapper_->base()->audio_processing(); RTC_DCHECK(apm_); + transmit_mixer_ = voe_wrapper_->base()->transmit_mixer(); + RTC_DCHECK(transmit_mixer_); + // Save the default AGC configuration settings. This must happen before // calling ApplyOptions or the default will be overwritten. - int error = voe_wrapper_->processing()->GetAgcConfig(default_agc_config_); - RTC_DCHECK_EQ(0, error); + default_agc_config_ = webrtc::apm_helpers::GetAgcConfig(apm_); // Set default engine options. { @@ -680,9 +684,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { // kEcConference is AEC with high suppression. webrtc::EcModes ec_mode = webrtc::kEcConference; - webrtc::AecmModes aecm_mode = webrtc::kAecmSpeakerphone; webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; - webrtc::NsModes ns_mode = webrtc::kNsHighSuppression; if (options.aecm_generate_comfort_noise) { LOG(LS_VERBOSE) << "Comfort noise explicitly set to " << *options.aecm_generate_comfort_noise @@ -729,8 +731,6 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { options.intelligibility_enhancer = rtc::Optional(false); #endif - webrtc::VoEAudioProcessing* voep = voe_wrapper_->processing(); - if (options.echo_cancellation) { // Check if platform supports built-in EC. Currently only supported on // Android and in combination with Java based audio layer. @@ -751,26 +751,14 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { LOG(LS_INFO) << "Disabling EC since built-in EC will be used instead"; } } - if (voep->SetEcStatus(*options.echo_cancellation, ec_mode) == -1) { - LOG_RTCERR2(SetEcStatus, *options.echo_cancellation, ec_mode); - return false; - } else { - LOG(LS_INFO) << "Echo control set to " << *options.echo_cancellation - << " with mode " << ec_mode; - } + webrtc::apm_helpers::SetEcStatus( + apm(), *options.echo_cancellation, ec_mode); #if !defined(ANDROID) - // TODO(ajm): Remove the error return on Android from webrtc. - if (voep->SetEcMetricsStatus(*options.echo_cancellation) == -1) { - LOG_RTCERR1(SetEcMetricsStatus, *options.echo_cancellation); - return false; - } + webrtc::apm_helpers::SetEcMetricsStatus(apm(), *options.echo_cancellation); #endif if (ec_mode == webrtc::kEcAecm) { bool cn = options.aecm_generate_comfort_noise.value_or(false); - if (voep->SetAecmMode(aecm_mode, cn) != 0) { - LOG_RTCERR2(SetAecmMode, aecm_mode, cn); - return false; - } + webrtc::apm_helpers::SetAecmMode(apm(), cn); } } @@ -785,17 +773,12 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { LOG(LS_INFO) << "Disabling AGC since built-in AGC will be used instead"; } } - if (voep->SetAgcStatus(*options.auto_gain_control, agc_mode) == -1) { - LOG_RTCERR2(SetAgcStatus, *options.auto_gain_control, agc_mode); - return false; - } else { - LOG(LS_INFO) << "Auto gain set to " << *options.auto_gain_control - << " with mode " << agc_mode; - } + webrtc::apm_helpers::SetAgcStatus( + apm(), adm(), *options.auto_gain_control, agc_mode); } if (options.tx_agc_target_dbov || options.tx_agc_digital_compression_gain || - options.tx_agc_limiter) { + options.tx_agc_limiter || options.adjust_agc_delta) { // Override default_agc_config_. Generally, an unset option means "leave // the VoE bits alone" in this function, so we want whatever is set to be // stored as the new "default". If we didn't, then setting e.g. @@ -811,13 +794,15 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { default_agc_config_.digitalCompressionGaindB); default_agc_config_.limiterEnable = options.tx_agc_limiter.value_or(default_agc_config_.limiterEnable); - if (voe_wrapper_->processing()->SetAgcConfig(default_agc_config_) == -1) { - LOG_RTCERR3(SetAgcConfig, - default_agc_config_.targetLeveldBOv, - default_agc_config_.digitalCompressionGaindB, - default_agc_config_.limiterEnable); - return false; + + webrtc::AgcConfig config = default_agc_config_; + if (options.adjust_agc_delta) { + config.targetLeveldBOv -= *options.adjust_agc_delta; + LOG(LS_INFO) << "Adjusting AGC level from default -" + << default_agc_config_.targetLeveldBOv << "dB to -" + << config.targetLeveldBOv << "dB"; } + webrtc::apm_helpers::SetAgcConfig(apm_, config); } if (options.intelligibility_enhancer) { @@ -840,22 +825,12 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { LOG(LS_INFO) << "Disabling NS since built-in NS will be used instead"; } } - if (voep->SetNsStatus(*options.noise_suppression, ns_mode) == -1) { - LOG_RTCERR2(SetNsStatus, *options.noise_suppression, ns_mode); - return false; - } else { - LOG(LS_INFO) << "Noise suppression set to " << *options.noise_suppression - << " with mode " << ns_mode; - } + webrtc::apm_helpers::SetNsStatus(apm(), *options.noise_suppression); } if (options.stereo_swapping) { LOG(LS_INFO) << "Stereo swapping enabled? " << *options.stereo_swapping; - voep->EnableStereoChannelSwapping(*options.stereo_swapping); - if (voep->IsStereoChannelSwappingEnabled() != *options.stereo_swapping) { - LOG_RTCERR1(EnableStereoChannelSwapping, *options.stereo_swapping); - return false; - } + transmit_mixer()->EnableStereoChannelSwapping(*options.stereo_swapping); } if (options.audio_jitter_buffer_max_packets) { @@ -874,17 +849,8 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (options.typing_detection) { LOG(LS_INFO) << "Typing detection is enabled? " << *options.typing_detection; - if (voep->SetTypingDetectionStatus(*options.typing_detection) == -1) { - // In case of error, log the info and continue - LOG_RTCERR1(SetTypingDetectionStatus, *options.typing_detection); - } - } - - if (options.adjust_agc_delta) { - LOG(LS_INFO) << "Adjust agc delta is " << *options.adjust_agc_delta; - if (!AdjustAgcLevel(*options.adjust_agc_delta)) { - return false; - } + webrtc::apm_helpers::SetTypingDetectionStatus( + apm(), *options.typing_detection); } webrtc::Config config; @@ -1067,25 +1033,6 @@ void WebRtcVoiceEngine::UnregisterChannel(WebRtcVoiceMediaChannel* channel) { channels_.erase(it); } -// Adjusts the default AGC target level by the specified delta. -// NB: If we start messing with other config fields, we'll want -// to save the current webrtc::AgcConfig as well. -bool WebRtcVoiceEngine::AdjustAgcLevel(int delta) { - RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); - webrtc::AgcConfig config = default_agc_config_; - config.targetLeveldBOv -= delta; - - LOG(LS_INFO) << "Adjusting AGC level from default -" - << default_agc_config_.targetLeveldBOv << "dB to -" - << config.targetLeveldBOv << "dB"; - - if (voe_wrapper_->processing()->SetAgcConfig(config) == -1) { - LOG_RTCERR1(SetAgcConfig, config.targetLeveldBOv); - return false; - } - return true; -} - bool WebRtcVoiceEngine::StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) { RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); @@ -1148,6 +1095,12 @@ webrtc::AudioProcessing* WebRtcVoiceEngine::apm() { return apm_; } +webrtc::voe::TransmitMixer* WebRtcVoiceEngine::transmit_mixer() { + RTC_DCHECK(worker_thread_checker_.CalledOnValidThread()); + RTC_DCHECK(transmit_mixer_); + return transmit_mixer_; +} + AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const { PayloadTypeMapper mapper; AudioCodecs out; @@ -1884,7 +1837,7 @@ bool WebRtcVoiceMediaChannel::SetOptions(const AudioOptions& options) { it.second->RecreateAudioSendStream(audio_network_adatptor_config); } - LOG(LS_INFO) << "Set voice channel options. Current options: " + LOG(LS_INFO) << "Set voice channel options. Current options: " << options_.ToString(); return true; } diff --git a/webrtc/media/engine/webrtcvoiceengine.h b/webrtc/media/engine/webrtcvoiceengine.h index ea48544924..0eeef482ab 100644 --- a/webrtc/media/engine/webrtcvoiceengine.h +++ b/webrtc/media/engine/webrtcvoiceengine.h @@ -30,6 +30,12 @@ #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/pc/channel.h" +namespace webrtc { +namespace voe { +class TransmitMixer; +} // namespace voe +} // namespace webrtc + namespace cricket { class AudioDeviceModule; @@ -75,10 +81,6 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { void RegisterChannel(WebRtcVoiceMediaChannel* channel); void UnregisterChannel(WebRtcVoiceMediaChannel* channel); - // Called by WebRtcVoiceMediaChannel to set a gain offset from - // the default AGC target level. - bool AdjustAgcLevel(int delta); - VoEWrapper* voe() { return voe_wrapper_.get(); } int GetLastEngineError(); @@ -109,6 +111,7 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { int CreateVoEChannel(); webrtc::AudioDeviceModule* adm(); webrtc::AudioProcessing* apm(); + webrtc::voe::TransmitMixer* transmit_mixer(); AudioCodecs CollectRecvCodecs() const; @@ -120,6 +123,8 @@ class WebRtcVoiceEngine final : public webrtc::TraceCallback { rtc::scoped_refptr decoder_factory_; // Reference to the APM, owned by VoE. webrtc::AudioProcessing* apm_ = nullptr; + // Reference to the TransmitMixer, owned by VoE. + webrtc::voe::TransmitMixer* transmit_mixer_ = nullptr; // The primary instance of WebRtc VoiceEngine. std::unique_ptr voe_wrapper_; rtc::scoped_refptr audio_state_; diff --git a/webrtc/media/engine/webrtcvoiceengine_unittest.cc b/webrtc/media/engine/webrtcvoiceengine_unittest.cc index 0bb3501339..a08ac6a92e 100644 --- a/webrtc/media/engine/webrtcvoiceengine_unittest.cc +++ b/webrtc/media/engine/webrtcvoiceengine_unittest.cc @@ -13,7 +13,6 @@ #include "webrtc/api/audio_codecs/builtin_audio_decoder_factory.h" #include "webrtc/base/arraysize.h" #include "webrtc/base/byteorder.h" -#include "webrtc/base/gunit.h" #include "webrtc/base/safe_conversions.h" #include "webrtc/call/call.h" #include "webrtc/logging/rtc_event_log/rtc_event_log.h" @@ -29,6 +28,8 @@ #include "webrtc/modules/audio_processing/include/mock_audio_processing.h" #include "webrtc/pc/channel.h" #include "webrtc/test/field_trial.h" +#include "webrtc/test/gtest.h" +#include "webrtc/voice_engine/transmit_mixer.h" using testing::Return; using testing::StrictMock; @@ -58,14 +59,20 @@ constexpr int kRtpHistoryMs = 5000; class FakeVoEWrapper : public cricket::VoEWrapper { public: explicit FakeVoEWrapper(cricket::FakeWebRtcVoiceEngine* engine) - : cricket::VoEWrapper(engine, // processing - engine, // base + : cricket::VoEWrapper(engine, // base engine, // codec engine, // hw engine) { // volume } }; +class MockTransmitMixer : public webrtc::voe::TransmitMixer { + public: + MockTransmitMixer() = default; + virtual ~MockTransmitMixer() = default; + + MOCK_METHOD1(EnableStereoChannelSwapping, void(bool enable)); +}; } // namespace // Tests that our stub library "works". @@ -76,11 +83,14 @@ TEST(WebRtcVoiceEngineTestStubLibrary, StartupShutdown) { EXPECT_CALL(adm, BuiltInAECIsAvailable()).WillOnce(Return(false)); EXPECT_CALL(adm, BuiltInAGCIsAvailable()).WillOnce(Return(false)); EXPECT_CALL(adm, BuiltInNSIsAvailable()).WillOnce(Return(false)); + EXPECT_CALL(adm, SetAGC(true)).WillOnce(Return(0)); StrictMock apm; EXPECT_CALL(apm, ApplyConfig(testing::_)); EXPECT_CALL(apm, SetExtraOptions(testing::_)); EXPECT_CALL(apm, Initialize()).WillOnce(Return(0)); - cricket::FakeWebRtcVoiceEngine voe(&apm); + StrictMock transmit_mixer; + EXPECT_CALL(transmit_mixer, EnableStereoChannelSwapping(false)); + cricket::FakeWebRtcVoiceEngine voe(&apm, &transmit_mixer); EXPECT_FALSE(voe.IsInited()); { cricket::WebRtcVoiceEngine engine( @@ -105,16 +115,35 @@ class WebRtcVoiceEngineTestFake : public testing::Test { WebRtcVoiceEngineTestFake() : WebRtcVoiceEngineTestFake("") {} explicit WebRtcVoiceEngineTestFake(const char* field_trials) - : call_(webrtc::Call::Config(&event_log_)), voe_(&apm_), + : apm_gc_(*apm_.gain_control()), apm_ec_(*apm_.echo_cancellation()), + apm_ns_(*apm_.noise_suppression()), apm_vd_(*apm_.voice_detection()), + call_(webrtc::Call::Config(&event_log_)), voe_(&apm_, &transmit_mixer_), override_field_trials_(field_trials) { + // AudioDeviceModule. EXPECT_CALL(adm_, AddRef()).WillOnce(Return(0)); EXPECT_CALL(adm_, Release()).WillOnce(Return(0)); EXPECT_CALL(adm_, BuiltInAECIsAvailable()).WillOnce(Return(false)); EXPECT_CALL(adm_, BuiltInAGCIsAvailable()).WillOnce(Return(false)); EXPECT_CALL(adm_, BuiltInNSIsAvailable()).WillOnce(Return(false)); + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + // AudioProcessing. EXPECT_CALL(apm_, ApplyConfig(testing::_)); EXPECT_CALL(apm_, SetExtraOptions(testing::_)); EXPECT_CALL(apm_, Initialize()).WillOnce(Return(0)); + // Default Options. + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_vd_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(false)); + // Init does not overwrite default AGC config. + EXPECT_CALL(apm_gc_, target_level_dbfs()).WillOnce(Return(1)); + EXPECT_CALL(apm_gc_, compression_gain_db()).WillRepeatedly(Return(5)); + EXPECT_CALL(apm_gc_, is_limiter_enabled()).WillRepeatedly(Return(true)); + EXPECT_CALL(apm_gc_, set_target_level_dbfs(1)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, set_compression_gain_db(5)).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0)); // TODO(kwiberg): We should use a mock AudioDecoderFactory, but a bunch of // the tests here probe the specific set of codecs provided by the builtin // factory. Those tests should probably be moved elsewhere. @@ -123,6 +152,8 @@ class WebRtcVoiceEngineTestFake : public testing::Test { new FakeVoEWrapper(&voe_))); send_parameters_.codecs.push_back(kPcmuCodec); recv_parameters_.codecs.push_back(kPcmuCodec); + // Default Options. + EXPECT_TRUE(IsHighPassFilterEnabled()); } bool SetupChannel() { @@ -607,6 +638,11 @@ class WebRtcVoiceEngineTestFake : public testing::Test { protected: StrictMock adm_; StrictMock apm_; + webrtc::test::MockGainControl& apm_gc_; + webrtc::test::MockEchoCancellation& apm_ec_; + webrtc::test::MockNoiseSuppression& apm_ns_; + webrtc::test::MockVoiceDetection& apm_vd_; + StrictMock transmit_mixer_; webrtc::RtcEventLogNullImpl event_log_; cricket::FakeCall call_; cricket::FakeWebRtcVoiceEngine voe_; @@ -2450,46 +2486,37 @@ TEST_F(WebRtcVoiceEngineTestFake, PlayoutWithMultipleStreams) { // and start sending on it. TEST_F(WebRtcVoiceEngineTestFake, CodianSend) { EXPECT_TRUE(SetupSendStream()); - cricket::AudioOptions options_adjust_agc; - options_adjust_agc.adjust_agc_delta = rtc::Optional(-10); - webrtc::AgcConfig agc_config; - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); - EXPECT_EQ(0, agc_config.targetLeveldBOv); - send_parameters_.options = options_adjust_agc; + send_parameters_.options.adjust_agc_delta = rtc::Optional(-10); + EXPECT_CALL(apm_gc_, + set_target_level_dbfs(11)).Times(2).WillRepeatedly(Return(0)); SetSendParameters(send_parameters_); SetSend(true); EXPECT_TRUE(GetSendStream(kSsrc1).IsSending()); - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); - EXPECT_EQ(agc_config.targetLeveldBOv, 10); // level was attenuated SetSend(false); EXPECT_FALSE(GetSendStream(kSsrc1).IsSending()); - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); } TEST_F(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) { EXPECT_TRUE(SetupSendStream()); EXPECT_CALL(adm_, BuiltInAGCIsAvailable()).Times(2).WillRepeatedly(Return(false)); - webrtc::AgcConfig agc_config; - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); - EXPECT_EQ(0, agc_config.targetLeveldBOv); + EXPECT_CALL(adm_, SetAGC(true)).Times(2).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).Times(2).WillOnce(Return(0)); send_parameters_.options.tx_agc_target_dbov = rtc::Optional(3); send_parameters_.options.tx_agc_digital_compression_gain = rtc::Optional(9); send_parameters_.options.tx_agc_limiter = rtc::Optional(true); send_parameters_.options.auto_gain_control = rtc::Optional(true); + EXPECT_CALL(apm_gc_, set_target_level_dbfs(3)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, set_compression_gain_db(9)).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0)); SetSendParameters(send_parameters_); - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); - EXPECT_EQ(3, agc_config.targetLeveldBOv); - EXPECT_EQ(9, agc_config.digitalCompressionGaindB); - EXPECT_TRUE(agc_config.limiterEnable); // Check interaction with adjust_agc_delta. Both should be respected, for // backwards compatibility. send_parameters_.options.adjust_agc_delta = rtc::Optional(-10); + EXPECT_CALL(apm_gc_, set_target_level_dbfs(13)).WillOnce(Return(0)); SetSendParameters(send_parameters_); - EXPECT_EQ(0, voe_.GetAgcConfig(agc_config)); - EXPECT_EQ(13, agc_config.targetLeveldBOv); } TEST_F(WebRtcVoiceEngineTestFake, SampleRatesViaOptions) { @@ -2957,202 +2984,102 @@ TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) { BuiltInAGCIsAvailable()).Times(4).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, BuiltInNSIsAvailable()).Times(2).WillRepeatedly(Return(false)); - bool ec_enabled; - webrtc::EcModes ec_mode; - webrtc::AecmModes aecm_mode; - bool cng_enabled; - bool agc_enabled; - webrtc::AgcModes agc_mode; - webrtc::AgcConfig agc_config; - bool ns_enabled; - webrtc::NsModes ns_mode; - bool stereo_swapping_enabled; - bool typing_detection_enabled; - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAecmMode(aecm_mode, cng_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetAgcConfig(agc_config); - voe_.GetNsStatus(ns_enabled, ns_mode); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(voe_.ec_metrics_enabled()); - EXPECT_FALSE(cng_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_EQ(0, agc_config.targetLeveldBOv); - EXPECT_TRUE(ns_enabled); - EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - EXPECT_EQ(ec_mode, webrtc::kEcConference); - EXPECT_EQ(ns_mode, webrtc::kNsHighSuppression); + EXPECT_EQ(50, voe_.GetNetEqCapacity()); EXPECT_FALSE(voe_.GetNetEqFastAccelerate()); // Nothing set in AudioOptions, so everything should be as default. send_parameters_.options = cricket::AudioOptions(); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAecmMode(aecm_mode, cng_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetAgcConfig(agc_config); - voe_.GetNsStatus(ns_enabled, ns_mode); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(voe_.ec_metrics_enabled()); - EXPECT_FALSE(cng_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_EQ(0, agc_config.targetLeveldBOv); - EXPECT_TRUE(ns_enabled); EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - EXPECT_EQ(ec_mode, webrtc::kEcConference); - EXPECT_EQ(ns_mode, webrtc::kNsHighSuppression); EXPECT_EQ(50, voe_.GetNetEqCapacity()); EXPECT_FALSE(voe_.GetNetEqFastAccelerate()); // Turn echo cancellation off + EXPECT_CALL(apm_ec_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(false)).WillOnce(Return(0)); send_parameters_.options.echo_cancellation = rtc::Optional(false); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - EXPECT_FALSE(ec_enabled); // Turn echo cancellation back on, with settings, and make sure // nothing else changed. + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); send_parameters_.options.echo_cancellation = rtc::Optional(true); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAecmMode(aecm_mode, cng_enabled); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetAgcConfig(agc_config); - voe_.GetNsStatus(ns_enabled, ns_mode); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(voe_.ec_metrics_enabled()); - EXPECT_TRUE(agc_enabled); - EXPECT_EQ(0, agc_config.targetLeveldBOv); - EXPECT_TRUE(ns_enabled); - EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_FALSE(stereo_swapping_enabled); - EXPECT_TRUE(typing_detection_enabled); - EXPECT_EQ(ec_mode, webrtc::kEcConference); - EXPECT_EQ(ns_mode, webrtc::kNsHighSuppression); // Turn on delay agnostic aec and make sure nothing change w.r.t. echo // control. + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); send_parameters_.options.delay_agnostic_aec = rtc::Optional(true); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAecmMode(aecm_mode, cng_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(voe_.ec_metrics_enabled()); - EXPECT_EQ(ec_mode, webrtc::kEcConference); // Turn off echo cancellation and delay agnostic aec. + EXPECT_CALL(apm_ec_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(false)).WillOnce(Return(0)); send_parameters_.options.delay_agnostic_aec = rtc::Optional(false); send_parameters_.options.extended_filter_aec = rtc::Optional(false); send_parameters_.options.echo_cancellation = rtc::Optional(false); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - EXPECT_FALSE(ec_enabled); + // Turning delay agnostic aec back on should also turn on echo cancellation. + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); send_parameters_.options.delay_agnostic_aec = rtc::Optional(true); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(voe_.ec_metrics_enabled()); - EXPECT_EQ(ec_mode, webrtc::kEcConference); // Turn off AGC + EXPECT_CALL(adm_, SetAGC(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); send_parameters_.options.auto_gain_control = rtc::Optional(false); SetSendParameters(send_parameters_); - voe_.GetAgcStatus(agc_enabled, agc_mode); - EXPECT_FALSE(agc_enabled); // Turn AGC back on + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); send_parameters_.options.auto_gain_control = rtc::Optional(true); send_parameters_.options.adjust_agc_delta = rtc::Optional(); SetSendParameters(send_parameters_); - voe_.GetAgcStatus(agc_enabled, agc_mode); - EXPECT_TRUE(agc_enabled); - voe_.GetAgcConfig(agc_config); - EXPECT_EQ(0, agc_config.targetLeveldBOv); // Turn off other options (and stereo swapping on). + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_vd_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(true)); send_parameters_.options.noise_suppression = rtc::Optional(false); send_parameters_.options.highpass_filter = rtc::Optional(false); send_parameters_.options.typing_detection = rtc::Optional(false); send_parameters_.options.stereo_swapping = rtc::Optional(true); SetSendParameters(send_parameters_); - voe_.GetNsStatus(ns_enabled, ns_mode); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_FALSE(ns_enabled); EXPECT_FALSE(IsHighPassFilterEnabled()); - EXPECT_FALSE(typing_detection_enabled); - EXPECT_TRUE(stereo_swapping_enabled); // Set options again to ensure it has no impact. + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_vd_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(transmit_mixer_, EnableStereoChannelSwapping(true)); SetSendParameters(send_parameters_); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_EQ(webrtc::kEcConference, ec_mode); - EXPECT_FALSE(ns_enabled); - EXPECT_EQ(webrtc::kNsHighSuppression, ns_mode); -} - -TEST_F(WebRtcVoiceEngineTestFake, DefaultOptions) { - EXPECT_TRUE(SetupSendStream()); - - bool ec_enabled; - webrtc::EcModes ec_mode; - bool agc_enabled; - webrtc::AgcModes agc_mode; - bool ns_enabled; - webrtc::NsModes ns_mode; - bool stereo_swapping_enabled; - bool typing_detection_enabled; - - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - stereo_swapping_enabled = voe_.IsStereoChannelSwappingEnabled(); - voe_.GetTypingDetectionStatus(typing_detection_enabled); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_TRUE(ns_enabled); - EXPECT_TRUE(IsHighPassFilterEnabled()); - EXPECT_TRUE(typing_detection_enabled); - EXPECT_FALSE(stereo_swapping_enabled); -} - -TEST_F(WebRtcVoiceEngineTestFake, InitDoesNotOverwriteDefaultAgcConfig) { - webrtc::AgcConfig set_config = {0}; - set_config.targetLeveldBOv = 3; - set_config.digitalCompressionGaindB = 9; - set_config.limiterEnable = true; - EXPECT_EQ(0, voe_.SetAgcConfig(set_config)); - - webrtc::AgcConfig config = {0}; - EXPECT_EQ(0, voe_.GetAgcConfig(config)); - EXPECT_EQ(set_config.targetLeveldBOv, config.targetLeveldBOv); - EXPECT_EQ(set_config.digitalCompressionGaindB, - config.digitalCompressionGaindB); - EXPECT_EQ(set_config.limiterEnable, config.limiterEnable); } TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { EXPECT_TRUE(SetupSendStream()); EXPECT_CALL(adm_, - BuiltInAECIsAvailable()).Times(9).WillRepeatedly(Return(false)); + BuiltInAECIsAvailable()).Times(8).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, - BuiltInAGCIsAvailable()).Times(9).WillRepeatedly(Return(false)); + BuiltInAGCIsAvailable()).Times(8).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, - BuiltInNSIsAvailable()).Times(9).WillRepeatedly(Return(false)); + BuiltInNSIsAvailable()).Times(8).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, RecordingIsInitialized()).Times(2).WillRepeatedly(Return(false)); EXPECT_CALL(adm_, Recording()).Times(2).WillRepeatedly(Return(false)); @@ -3180,6 +3107,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { parameters_options_all.options.echo_cancellation = rtc::Optional(true); parameters_options_all.options.auto_gain_control = rtc::Optional(true); parameters_options_all.options.noise_suppression = rtc::Optional(true); + EXPECT_CALL(adm_, SetAGC(true)).Times(2).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).Times(2).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).Times(2).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).Times(2).WillRepeatedly(Return(0)); + EXPECT_CALL(apm_ns_, Enable(true)).Times(2).WillRepeatedly(Return(0)); EXPECT_TRUE(channel1->SetSendParameters(parameters_options_all)); EXPECT_EQ(parameters_options_all.options, channel1->options()); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_all)); @@ -3189,6 +3121,11 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { cricket::AudioSendParameters parameters_options_no_ns = send_parameters_; parameters_options_no_ns.options.noise_suppression = rtc::Optional(false); + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); EXPECT_TRUE(channel1->SetSendParameters(parameters_options_no_ns)); cricket::AudioOptions expected_options = parameters_options_all.options; expected_options.echo_cancellation = rtc::Optional(true); @@ -3200,64 +3137,55 @@ TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) { cricket::AudioSendParameters parameters_options_no_agc = send_parameters_; parameters_options_no_agc.options.auto_gain_control = rtc::Optional(false); + EXPECT_CALL(adm_, SetAGC(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc)); expected_options.echo_cancellation = rtc::Optional(true); expected_options.auto_gain_control = rtc::Optional(false); expected_options.noise_suppression = rtc::Optional(true); EXPECT_EQ(expected_options, channel2->options()); + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); EXPECT_TRUE(channel_->SetSendParameters(parameters_options_all)); - bool ec_enabled; - webrtc::EcModes ec_mode; - bool agc_enabled; - webrtc::AgcModes agc_mode; - bool ns_enabled; - webrtc::NsModes ns_mode; - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_TRUE(ns_enabled); + EXPECT_CALL(adm_, SetAGC(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); channel1->SetSend(true); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_TRUE(agc_enabled); - EXPECT_FALSE(ns_enabled); + EXPECT_CALL(adm_, SetAGC(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0)); channel2->SetSend(true); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_TRUE(ns_enabled); // Make sure settings take effect while we are sending. - EXPECT_TRUE(channel_->SetSendParameters(parameters_options_all)); - EXPECT_CALL(apm_, ApplyConfig(testing::_)); - EXPECT_CALL(apm_, SetExtraOptions(testing::_)); cricket::AudioSendParameters parameters_options_no_agc_nor_ns = send_parameters_; parameters_options_no_agc_nor_ns.options.auto_gain_control = rtc::Optional(false); parameters_options_no_agc_nor_ns.options.noise_suppression = rtc::Optional(false); - channel2->SetSend(true); + EXPECT_CALL(adm_, SetAGC(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, Enable(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_ec_, enable_metrics(true)).WillOnce(Return(0)); + EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0)); + EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0)); EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc_nor_ns)); expected_options.echo_cancellation = rtc::Optional(true); expected_options.auto_gain_control = rtc::Optional(false); expected_options.noise_suppression = rtc::Optional(false); EXPECT_EQ(expected_options, channel2->options()); - voe_.GetEcStatus(ec_enabled, ec_mode); - voe_.GetAgcStatus(agc_enabled, agc_mode); - voe_.GetNsStatus(ns_enabled, ns_mode); - EXPECT_TRUE(ec_enabled); - EXPECT_FALSE(agc_enabled); - EXPECT_FALSE(ns_enabled); } // This test verifies DSCP settings are properly applied on voice media channel. diff --git a/webrtc/voice_engine/include/voe_base.h b/webrtc/voice_engine/include/voe_base.h index 95a3fd77b1..b8732129df 100644 --- a/webrtc/voice_engine/include/voe_base.h +++ b/webrtc/voice_engine/include/voe_base.h @@ -44,6 +44,9 @@ namespace webrtc { class AudioDeviceModule; class AudioProcessing; class AudioTransport; +namespace voe { +class TransmitMixer; +} // namespace voe // VoiceEngineObserver class WEBRTC_DLLEXPORT VoiceEngineObserver { @@ -143,6 +146,10 @@ class WEBRTC_DLLEXPORT VoEBase { // Returns NULL before Init() is called. virtual AudioDeviceModule* audio_device_module() = 0; + // This method is WIP - DO NOT USE! + // Returns NULL before Init() is called. + virtual voe::TransmitMixer* transmit_mixer() = 0; + // Terminates all VoiceEngine functions and releases allocated resources. // Returns 0. virtual int Terminate() = 0; diff --git a/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc b/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc index 360b76bb1e..0fc0d39c73 100644 --- a/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc +++ b/webrtc/voice_engine/test/auto_test/extended/agc_config_test.cc @@ -22,6 +22,7 @@ class AgcConfigTest : public AfterStreamingFixture { webrtc::AgcConfig default_agc_config_; }; +// Duplicated in apm_helpers_unittest.cc. TEST_F(AgcConfigTest, HasCorrectDefaultConfiguration) { webrtc::AgcConfig agc_config; @@ -33,6 +34,7 @@ TEST_F(AgcConfigTest, HasCorrectDefaultConfiguration) { EXPECT_EQ(default_agc_config_.limiterEnable, agc_config.limiterEnable); } +// Not needed anymore - we're not returning errors anymore, just logging. TEST_F(AgcConfigTest, DealsWithInvalidParameters) { webrtc::AgcConfig agc_config = default_agc_config_; agc_config.digitalCompressionGaindB = 91; @@ -47,6 +49,7 @@ TEST_F(AgcConfigTest, DealsWithInvalidParameters) { EXPECT_EQ(VE_APM_ERROR, voe_base_->LastError()); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AgcConfigTest, CanGetAndSetAgcStatus) { webrtc::AgcConfig agc_config; agc_config.digitalCompressionGaindB = 17; diff --git a/webrtc/voice_engine/test/auto_test/extended/ec_metrics_test.cc b/webrtc/voice_engine/test/auto_test/extended/ec_metrics_test.cc index 758a0850ed..7ad565e9d8 100644 --- a/webrtc/voice_engine/test/auto_test/extended/ec_metrics_test.cc +++ b/webrtc/voice_engine/test/auto_test/extended/ec_metrics_test.cc @@ -13,6 +13,7 @@ class EcMetricsTest : public AfterStreamingFixture { }; +// Duplicated in apm_helpers_unittest.cc. TEST_F(EcMetricsTest, EcMetricsAreOnByDefault) { // AEC must be enabled fist. EXPECT_EQ(0, voe_apm_->SetEcStatus(true, webrtc::kEcAec)); @@ -22,6 +23,7 @@ TEST_F(EcMetricsTest, EcMetricsAreOnByDefault) { EXPECT_TRUE(enabled); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(EcMetricsTest, CanEnableAndDisableEcMetrics) { // AEC must be enabled fist. EXPECT_EQ(0, voe_apm_->SetEcStatus(true, webrtc::kEcAec)); @@ -35,6 +37,8 @@ TEST_F(EcMetricsTest, CanEnableAndDisableEcMetrics) { ASSERT_FALSE(ec_on); } +// TODO(solenberg): Do we have higher or lower level tests that verify metrics? +// It's not the right test for this level. TEST_F(EcMetricsTest, ManualTestEcMetrics) { SwitchToManualMicrophone(); @@ -63,6 +67,7 @@ TEST_F(EcMetricsTest, ManualTestEcMetrics) { EXPECT_EQ(0, voe_apm_->SetEcMetricsStatus(false)); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(EcMetricsTest, GetEcMetricsFailsIfEcNotEnabled) { int dummy = 0; EXPECT_EQ(0, voe_apm_->SetEcMetricsStatus(true)); @@ -70,6 +75,7 @@ TEST_F(EcMetricsTest, GetEcMetricsFailsIfEcNotEnabled) { EXPECT_EQ(VE_APM_ERROR, voe_base_->LastError()); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(EcMetricsTest, GetEcDelayMetricsFailsIfEcNotEnabled) { int dummy = 0; float dummy_f = 0; @@ -78,6 +84,8 @@ TEST_F(EcMetricsTest, GetEcDelayMetricsFailsIfEcNotEnabled) { EXPECT_EQ(VE_APM_ERROR, voe_base_->LastError()); } +// TODO(solenberg): Do we have higher or lower level tests that verify metrics? +// It's not the right test for this level. TEST_F(EcMetricsTest, ManualVerifyEcDelayMetrics) { SwitchToManualMicrophone(); TEST_LOG("Verify EC Delay metrics:"); diff --git a/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc b/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc index 99d2971724..4aab3639b9 100644 --- a/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc +++ b/webrtc/voice_engine/test/auto_test/standard/audio_processing_test.cc @@ -101,6 +101,7 @@ class AudioProcessingTest : public AfterStreamingFixture { #if !defined(WEBRTC_IOS) && !defined(WEBRTC_ANDROID) +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, AgcIsOnByDefault) { bool agc_enabled = false; webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; @@ -110,12 +111,14 @@ TEST_F(AudioProcessingTest, AgcIsOnByDefault) { EXPECT_EQ(webrtc::kAgcAdaptiveAnalog, agc_mode); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, CanEnableAgcWithAllModes) { TryEnablingAgcWithMode(webrtc::kAgcAdaptiveDigital); TryEnablingAgcWithMode(webrtc::kAgcAdaptiveAnalog); TryEnablingAgcWithMode(webrtc::kAgcFixedDigital); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, EcIsDisabledAndAecIsDefaultEcMode) { bool ec_enabled = true; webrtc::EcModes ec_mode = webrtc::kEcDefault; @@ -125,14 +128,17 @@ TEST_F(AudioProcessingTest, EcIsDisabledAndAecIsDefaultEcMode) { EXPECT_EQ(webrtc::kEcAec, ec_mode); } +// Not needed anymore - apm_helpers::SetEcStatus() doesn't take kEcAec. TEST_F(AudioProcessingTest, EnablingEcAecShouldEnableEcAec) { TryEnablingEcWithMode(webrtc::kEcAec, webrtc::kEcAec); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, EnablingEcConferenceShouldEnableEcAec) { TryEnablingEcWithMode(webrtc::kEcConference, webrtc::kEcAec); } +// Not needed anymore - apm_helpers::SetEcStatus() doesn't take kEcDefault. TEST_F(AudioProcessingTest, EcModeIsPreservedWhenEcIsTurnedOff) { TryEnablingEcWithMode(webrtc::kEcConference, webrtc::kEcAec); @@ -146,6 +152,7 @@ TEST_F(AudioProcessingTest, EcModeIsPreservedWhenEcIsTurnedOff) { EXPECT_EQ(webrtc::kEcAec, ec_mode); } +// Not needed anymore - apm_helpers::SetEcStatus() doesn't take kEcDefault. TEST_F(AudioProcessingTest, CanEnableAndDisableEcModeSeveralTimesInARow) { for (int i = 0; i < 10; i++) { EXPECT_EQ(0, voe_apm_->SetEcStatus(true)); @@ -162,11 +169,13 @@ TEST_F(AudioProcessingTest, CanEnableAndDisableEcModeSeveralTimesInARow) { #endif // !WEBRTC_IOS && !WEBRTC_ANDROID +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, EnablingEcAecmShouldEnableEcAecm) { // This one apparently applies to Android and iPhone as well. TryEnablingEcWithMode(webrtc::kEcAecm, webrtc::kEcAecm); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, EcAecmModeIsEnabledAndSpeakerphoneByDefault) { bool cng_enabled = false; webrtc::AecmModes aecm_mode = webrtc::kAecmEarpiece; @@ -177,6 +186,7 @@ TEST_F(AudioProcessingTest, EcAecmModeIsEnabledAndSpeakerphoneByDefault) { EXPECT_EQ(webrtc::kAecmSpeakerphone, aecm_mode); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, CanSetAecmMode) { EXPECT_EQ(0, voe_apm_->SetEcStatus(true, webrtc::kEcAecm)); @@ -189,6 +199,7 @@ TEST_F(AudioProcessingTest, CanSetAecmMode) { TryEnablingAecmWithMode(webrtc::kAecmSpeakerphone, false); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, NsIsOffWithModerateSuppressionByDefault) { bool ns_status = true; webrtc::NsModes ns_mode = webrtc::kNsDefault; @@ -198,6 +209,7 @@ TEST_F(AudioProcessingTest, NsIsOffWithModerateSuppressionByDefault) { EXPECT_EQ(webrtc::kNsModerateSuppression, ns_mode); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, CanSetNsMode) { // Concrete suppression values map to themselves. TryEnablingNsWithMode(webrtc::kNsHighSuppression, @@ -216,6 +228,8 @@ TEST_F(AudioProcessingTest, CanSetNsMode) { webrtc::kNsModerateSuppression); } +// TODO(solenberg): Duplicate this test at the voe::Channel layer. +// Not needed anymore - API is unused. TEST_F(AudioProcessingTest, VadIsDisabledByDefault) { bool vad_enabled; bool disabled_dtx; @@ -227,6 +241,7 @@ TEST_F(AudioProcessingTest, VadIsDisabledByDefault) { EXPECT_FALSE(vad_enabled); } +// Not needed anymore - API is unused. TEST_F(AudioProcessingTest, VoiceActivityIndicatorReturns1WithSpeechOn) { // This sleep is necessary since the voice detection algorithm needs some // time to detect the speech from the fake microphone. @@ -234,6 +249,7 @@ TEST_F(AudioProcessingTest, VoiceActivityIndicatorReturns1WithSpeechOn) { EXPECT_EQ(1, voe_apm_->VoiceActivityIndicator(channel_)); } +// Not needed anymore - API is unused. TEST_F(AudioProcessingTest, CanSetDelayOffset) { voe_apm_->SetDelayOffsetMs(50); EXPECT_EQ(50, voe_apm_->DelayOffsetMs()); @@ -241,10 +257,13 @@ TEST_F(AudioProcessingTest, CanSetDelayOffset) { EXPECT_EQ(-50, voe_apm_->DelayOffsetMs()); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, HighPassFilterIsOnByDefault) { EXPECT_TRUE(voe_apm_->IsHighPassFilterEnabled()); } +// TODO(solenberg): Check that sufficient testing is done in APM. +// Not needed anymore - API is unused. TEST_F(AudioProcessingTest, CanSetHighPassFilter) { EXPECT_EQ(0, voe_apm_->EnableHighPassFilter(true)); EXPECT_TRUE(voe_apm_->IsHighPassFilterEnabled()); @@ -252,10 +271,12 @@ TEST_F(AudioProcessingTest, CanSetHighPassFilter) { EXPECT_FALSE(voe_apm_->IsHighPassFilterEnabled()); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, StereoChannelSwappingIsOffByDefault) { EXPECT_FALSE(voe_apm_->IsStereoChannelSwappingEnabled()); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, CanSetStereoChannelSwapping) { voe_apm_->EnableStereoChannelSwapping(true); EXPECT_TRUE(voe_apm_->IsStereoChannelSwappingEnabled()); @@ -263,6 +284,7 @@ TEST_F(AudioProcessingTest, CanSetStereoChannelSwapping) { EXPECT_FALSE(voe_apm_->IsStereoChannelSwappingEnabled()); } +// TODO(solenberg): Check that sufficient testing is done in APM. TEST_F(AudioProcessingTest, CanStartAndStopDebugRecording) { std::string output_path = webrtc::test::OutputPath(); std::string output_file = output_path + "apm_debug.txt"; @@ -274,6 +296,7 @@ TEST_F(AudioProcessingTest, CanStartAndStopDebugRecording) { #if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, AgcIsOffByDefaultAndDigital) { bool agc_enabled = true; webrtc::AgcModes agc_mode = webrtc::kAgcAdaptiveAnalog; @@ -283,16 +306,19 @@ TEST_F(AudioProcessingTest, AgcIsOffByDefaultAndDigital) { EXPECT_EQ(webrtc::kAgcAdaptiveDigital, agc_mode); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, CanEnableAgcInAdaptiveDigitalMode) { TryEnablingAgcWithMode(webrtc::kAgcAdaptiveDigital); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, AgcIsPossibleExceptInAdaptiveAnalogMode) { EXPECT_EQ(-1, voe_apm_->SetAgcStatus(true, webrtc::kAgcAdaptiveAnalog)); EXPECT_EQ(0, voe_apm_->SetAgcStatus(true, webrtc::kAgcFixedDigital)); EXPECT_EQ(0, voe_apm_->SetAgcStatus(true, webrtc::kAgcAdaptiveDigital)); } +// Duplicated in apm_helpers_unittest.cc. TEST_F(AudioProcessingTest, EcIsDisabledAndAecmIsDefaultEcMode) { bool ec_enabled = true; webrtc::EcModes ec_mode = webrtc::kEcDefault; @@ -302,6 +328,7 @@ TEST_F(AudioProcessingTest, EcIsDisabledAndAecmIsDefaultEcMode) { EXPECT_EQ(webrtc::kEcAecm, ec_mode); } +// Not needed anymore - API is unused. TEST_F(AudioProcessingTest, TestVoiceActivityDetection) { TryDetectingSilence(); TryDetectingSpeechAfterSilence(); diff --git a/webrtc/voice_engine/transmit_mixer.cc b/webrtc/voice_engine/transmit_mixer.cc index 76c10ba57f..5deee42854 100644 --- a/webrtc/voice_engine/transmit_mixer.cc +++ b/webrtc/voice_engine/transmit_mixer.cc @@ -176,31 +176,12 @@ TransmitMixer::Destroy(TransmitMixer*& mixer) } TransmitMixer::TransmitMixer(uint32_t instanceId) : - _engineStatisticsPtr(NULL), - _channelManagerPtr(NULL), - audioproc_(NULL), - _voiceEngineObserverPtr(NULL), - _processThreadPtr(NULL), // Avoid conflict with other channels by adding 1024 - 1026, // won't use as much as 1024 channels. _filePlayerId(instanceId + 1024), _fileRecorderId(instanceId + 1025), _fileCallRecorderId(instanceId + 1026), - _filePlaying(false), - _fileRecording(false), - _fileCallRecording(false), - _audioLevel(), -#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION - _typingNoiseWarningPending(false), - _typingNoiseDetected(false), -#endif - _saturationWarning(false), - _instanceId(instanceId), - _mixFileWithMicrophone(false), - _captureLevel(0), - _mute(false), - stereo_codec_(false), - swap_stereo_channels_(false) + _instanceId(instanceId) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1), "TransmitMixer::TransmitMixer() - ctor"); diff --git a/webrtc/voice_engine/transmit_mixer.h b/webrtc/voice_engine/transmit_mixer.h index 35003848d1..25f02b3bbc 100644 --- a/webrtc/voice_engine/transmit_mixer.h +++ b/webrtc/voice_engine/transmit_mixer.h @@ -138,15 +138,15 @@ public: // FileCallback - void PlayNotification(int32_t id, - uint32_t durationMs); + void PlayNotification(const int32_t id, + const uint32_t durationMs); - void RecordNotification(int32_t id, - uint32_t durationMs); + void RecordNotification(const int32_t id, + const uint32_t durationMs); - void PlayFileEnded(int32_t id); + void PlayFileEnded(const int32_t id); - void RecordFileEnded(int32_t id); + void RecordFileEnded(const int32_t id); #if WEBRTC_VOICE_ENGINE_TYPING_DETECTION // Typing detection @@ -158,9 +158,13 @@ public: int typeEventDelay); #endif - void EnableStereoChannelSwapping(bool enable); + // Virtual to allow mocking. + virtual void EnableStereoChannelSwapping(bool enable); bool IsStereoChannelSwappingEnabled(); +protected: + TransmitMixer() = default; + private: TransmitMixer(uint32_t instanceId); @@ -185,11 +189,11 @@ private: #endif // uses - Statistics* _engineStatisticsPtr; - ChannelManager* _channelManagerPtr; - AudioProcessing* audioproc_; - VoiceEngineObserver* _voiceEngineObserverPtr; - ProcessThread* _processThreadPtr; + Statistics* _engineStatisticsPtr = nullptr; + ChannelManager* _channelManagerPtr = nullptr; + AudioProcessing* audioproc_ = nullptr; + VoiceEngineObserver* _voiceEngineObserverPtr = nullptr; + ProcessThread* _processThreadPtr = nullptr; // owns MonitorModule _monitorModule; @@ -198,12 +202,12 @@ private: std::unique_ptr file_player_; std::unique_ptr file_recorder_; std::unique_ptr file_call_recorder_; - int _filePlayerId; - int _fileRecorderId; - int _fileCallRecorderId; - bool _filePlaying; - bool _fileRecording; - bool _fileCallRecording; + int _filePlayerId = 0; + int _fileRecorderId = 0; + int _fileCallRecorderId = 0; + bool _filePlaying = false; + bool _fileRecording = false; + bool _fileCallRecording = false; voe::AudioLevel _audioLevel; // protect file instances and their variables in MixedParticipants() rtc::CriticalSection _critSect; @@ -211,21 +215,19 @@ private: #if WEBRTC_VOICE_ENGINE_TYPING_DETECTION webrtc::TypingDetection _typingDetection; - bool _typingNoiseWarningPending; - bool _typingNoiseDetected; + bool _typingNoiseWarningPending = false; + bool _typingNoiseDetected = false; #endif - bool _saturationWarning; + bool _saturationWarning = false; - int _instanceId; - bool _mixFileWithMicrophone; - uint32_t _captureLevel; - bool _mute; - bool stereo_codec_; - bool swap_stereo_channels_; + int _instanceId = 0; + bool _mixFileWithMicrophone = false; + uint32_t _captureLevel = 0; + bool _mute = false; + bool stereo_codec_ = false; + bool swap_stereo_channels_ = false; }; - } // namespace voe - } // namespace webrtc #endif // WEBRTC_VOICE_ENGINE_TRANSMIT_MIXER_H diff --git a/webrtc/voice_engine/voe_base_impl.h b/webrtc/voice_engine/voe_base_impl.h index a9f06e4132..b544c09f9a 100644 --- a/webrtc/voice_engine/voe_base_impl.h +++ b/webrtc/voice_engine/voe_base_impl.h @@ -38,6 +38,9 @@ class VoEBaseImpl : public VoEBase, AudioDeviceModule* audio_device_module() override { return shared_->audio_device(); } + voe::TransmitMixer* transmit_mixer() override { + return shared_->transmit_mixer(); + } int Terminate() override; int CreateChannel() override;