blob: 92b97b091540c4e584aad958d2215d2a0e494b92 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h"
#include <stddef.h>
#include <utility>
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "build/build_config.h"
#include "media/base/audio_parameters.h"
#include "third_party/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h"
#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
#include "third_party/webrtc/modules/audio_processing/typing_detection.h"
namespace blink {
namespace {
using NoiseSuppression = webrtc::AudioProcessing::Config::NoiseSuppression;
base::Optional<double> GetGainControlCompressionGain(
const base::Value& config) {
const base::Value* found = config.FindKey("gain_control_compression_gain_db");
if (!found)
return base::nullopt;
double gain = found->GetDouble();
DCHECK_GE(gain, 0.f);
return gain;
}
base::Optional<double> GetPreAmplifierGainFactor(const base::Value& config) {
const base::Value* found = config.FindKey("pre_amplifier_fixed_gain_factor");
if (!found)
return base::nullopt;
double factor = found->GetDouble();
DCHECK_GE(factor, 1.f);
return factor;
}
base::Optional<NoiseSuppression::Level> GetNoiseSuppressionLevel(
const base::Value& config) {
const base::Value* found = config.FindKey("noise_suppression_level");
if (!found)
return base::nullopt;
int level = found->GetInt();
DCHECK_GE(level, static_cast<int>(NoiseSuppression::kLow));
DCHECK_LE(level, static_cast<int>(NoiseSuppression::kVeryHigh));
return static_cast<NoiseSuppression::Level>(level);
}
void GetExtraConfigFromJson(
const std::string& audio_processing_platform_config_json,
base::Optional<double>* gain_control_compression_gain_db,
base::Optional<double>* pre_amplifier_fixed_gain_factor,
base::Optional<NoiseSuppression::Level>* noise_suppression_level) {
auto config = base::JSONReader::Read(audio_processing_platform_config_json);
if (!config) {
LOG(ERROR) << "Failed to parse platform config JSON.";
return;
}
*gain_control_compression_gain_db = GetGainControlCompressionGain(*config);
*pre_amplifier_fixed_gain_factor = GetPreAmplifierGainFactor(*config);
*noise_suppression_level = GetNoiseSuppressionLevel(*config);
}
} // namespace
AudioProcessingProperties::AudioProcessingProperties() = default;
AudioProcessingProperties::AudioProcessingProperties(
const AudioProcessingProperties& other) = default;
AudioProcessingProperties& AudioProcessingProperties::operator=(
const AudioProcessingProperties& other) = default;
void AudioProcessingProperties::DisableDefaultProperties() {
echo_cancellation_type = EchoCancellationType::kEchoCancellationDisabled;
goog_auto_gain_control = false;
goog_experimental_echo_cancellation = false;
goog_typing_noise_detection = false;
goog_noise_suppression = false;
goog_experimental_noise_suppression = false;
goog_highpass_filter = false;
goog_experimental_auto_gain_control = false;
}
bool AudioProcessingProperties::EchoCancellationEnabled() const {
return echo_cancellation_type !=
EchoCancellationType::kEchoCancellationDisabled;
}
bool AudioProcessingProperties::EchoCancellationIsWebRtcProvided() const {
return echo_cancellation_type == EchoCancellationType::kEchoCancellationAec3;
}
bool AudioProcessingProperties::HasSameReconfigurableSettings(
const AudioProcessingProperties& other) const {
return echo_cancellation_type == other.echo_cancellation_type;
}
bool AudioProcessingProperties::HasSameNonReconfigurableSettings(
const AudioProcessingProperties& other) const {
return disable_hw_noise_suppression == other.disable_hw_noise_suppression &&
goog_audio_mirroring == other.goog_audio_mirroring &&
goog_auto_gain_control == other.goog_auto_gain_control &&
goog_experimental_echo_cancellation ==
other.goog_experimental_echo_cancellation &&
goog_typing_noise_detection == other.goog_typing_noise_detection &&
goog_noise_suppression == other.goog_noise_suppression &&
goog_experimental_noise_suppression ==
other.goog_experimental_noise_suppression &&
goog_highpass_filter == other.goog_highpass_filter &&
goog_experimental_auto_gain_control ==
other.goog_experimental_auto_gain_control;
}
media::AudioProcessingSettings
AudioProcessingProperties::ToAudioProcessingSettings() const {
media::AudioProcessingSettings out;
auto convert_type =
[](EchoCancellationType type) -> media::EchoCancellationType {
switch (type) {
case EchoCancellationType::kEchoCancellationDisabled:
return media::EchoCancellationType::kDisabled;
case EchoCancellationType::kEchoCancellationAec3:
return media::EchoCancellationType::kAec3;
case EchoCancellationType::kEchoCancellationSystem:
return media::EchoCancellationType::kSystemAec;
}
};
out.echo_cancellation = convert_type(echo_cancellation_type);
out.noise_suppression =
goog_noise_suppression ? (goog_experimental_noise_suppression
? media::NoiseSuppressionType::kExperimental
: media::NoiseSuppressionType::kDefault)
: media::NoiseSuppressionType::kDisabled;
out.automatic_gain_control =
goog_auto_gain_control
? (goog_experimental_auto_gain_control
? media::AutomaticGainControlType::kExperimental
: media::AutomaticGainControlType::kDefault)
: media::AutomaticGainControlType::kDisabled;
out.high_pass_filter = goog_highpass_filter;
out.typing_detection = goog_typing_noise_detection;
return out;
}
void EnableTypingDetection(AudioProcessing::Config* apm_config,
webrtc::TypingDetection* typing_detector) {
apm_config->voice_detection.enabled = true;
// Configure the update period to 1s (100 * 10ms) in the typing detector.
typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
}
void StartEchoCancellationDump(AudioProcessing* audio_processing,
base::File aec_dump_file,
rtc::TaskQueue* worker_queue) {
DCHECK(aec_dump_file.IsValid());
FILE* stream = base::FileToFILE(std::move(aec_dump_file), "w");
if (!stream) {
LOG(DFATAL) << "Failed to open AEC dump file";
return;
}
auto aec_dump = webrtc::AecDumpFactory::Create(
stream, -1 /* max_log_size_bytes */, worker_queue);
if (!aec_dump) {
LOG(ERROR) << "Failed to start AEC debug recording";
return;
}
audio_processing->AttachAecDump(std::move(aec_dump));
}
void StopEchoCancellationDump(AudioProcessing* audio_processing) {
audio_processing->DetachAecDump();
}
void ConfigAutomaticGainControl(
bool agc_enabled,
bool experimental_agc_enabled,
base::Optional<AdaptiveGainController2Properties> agc2_properties,
base::Optional<double> compression_gain_db,
AudioProcessing::Config& apm_config) {
const bool use_fixed_digital_agc2 = agc_enabled &&
!experimental_agc_enabled &&
compression_gain_db.has_value();
const bool use_hybrid_agc = agc2_properties.has_value();
const bool agc1_enabled =
agc_enabled && (use_hybrid_agc || !use_fixed_digital_agc2);
// Configure AGC1.
if (agc1_enabled) {
apm_config.gain_controller1.enabled = true;
apm_config.gain_controller1.mode =
#if defined(OS_ANDROID)
AudioProcessing::Config::GainController1::Mode::kFixedDigital;
#else
AudioProcessing::Config::GainController1::Mode::kAdaptiveAnalog;
#endif
}
// Configure AGC2.
if (experimental_agc_enabled) {
// Experimental AGC is enabled. Hybrid AGC may or may not be enabled. Config
// AGC2 with adaptive mode and the given options, while ignoring
// |use_fixed_digital_agc2|.
apm_config.gain_controller2.enabled = use_hybrid_agc;
apm_config.gain_controller2.fixed_digital.gain_db = 0.f;
apm_config.gain_controller2.adaptive_digital.enabled = true;
if (use_hybrid_agc) {
auto& adaptive_digital = apm_config.gain_controller2.adaptive_digital;
adaptive_digital.vad_probability_attack =
agc2_properties->vad_probability_attack;
using LevelEstimator =
AudioProcessing::Config::GainController2::LevelEstimator;
adaptive_digital.level_estimator = agc2_properties->use_peaks_not_rms
? LevelEstimator::kPeak
: LevelEstimator::kRms;
adaptive_digital.level_estimator_adjacent_speech_frames_threshold =
agc2_properties->level_estimator_speech_frames_threshold;
adaptive_digital.initial_saturation_margin_db =
agc2_properties->initial_saturation_margin_db;
adaptive_digital.extra_saturation_margin_db =
agc2_properties->extra_saturation_margin_db;
adaptive_digital.gain_applier_adjacent_speech_frames_threshold =
agc2_properties->gain_applier_speech_frames_threshold;
adaptive_digital.max_gain_change_db_per_second =
agc2_properties->max_gain_change_db_per_second;
adaptive_digital.max_output_noise_level_dbfs =
agc2_properties->max_output_noise_level_dbfs;
adaptive_digital.sse2_allowed = agc2_properties->sse2_allowed;
adaptive_digital.avx2_allowed = agc2_properties->avx2_allowed;
adaptive_digital.neon_allowed = agc2_properties->neon_allowed;
}
} else if (use_fixed_digital_agc2) {
// Experimental AGC is disabled, thus hybrid AGC is disabled. Config AGC2
// with fixed gain mode.
apm_config.gain_controller2.enabled = true;
apm_config.gain_controller2.fixed_digital.gain_db =
compression_gain_db.value();
apm_config.gain_controller2.adaptive_digital.enabled = false;
}
}
void PopulateApmConfig(
AudioProcessing::Config* apm_config,
const AudioProcessingProperties& properties,
const base::Optional<std::string>& audio_processing_platform_config_json,
base::Optional<double>* gain_control_compression_gain_db) {
// TODO(saza): When Chrome uses AGC2, handle all JSON config via the
// webrtc::AudioProcessing::Config, crbug.com/895814.
base::Optional<double> pre_amplifier_fixed_gain_factor;
base::Optional<NoiseSuppression::Level> noise_suppression_level;
if (audio_processing_platform_config_json.has_value()) {
GetExtraConfigFromJson(audio_processing_platform_config_json.value(),
gain_control_compression_gain_db,
&pre_amplifier_fixed_gain_factor,
&noise_suppression_level);
}
apm_config->high_pass_filter.enabled = properties.goog_highpass_filter;
if (pre_amplifier_fixed_gain_factor.has_value()) {
apm_config->pre_amplifier.enabled = true;
apm_config->pre_amplifier.fixed_gain_factor =
pre_amplifier_fixed_gain_factor.value();
}
if (properties.goog_noise_suppression) {
apm_config->noise_suppression.enabled = true;
apm_config->noise_suppression.level =
noise_suppression_level.value_or(NoiseSuppression::kHigh);
}
if (properties.EchoCancellationIsWebRtcProvided()) {
apm_config->echo_canceller.enabled = true;
#if defined(OS_ANDROID)
apm_config->echo_canceller.mobile_mode = true;
#else
apm_config->echo_canceller.mobile_mode = false;
#endif
}
}
} // namespace blink