blob: 08326c99985d1eadf630516546d7e6dd2037e1a8 [file] [log] [blame]
// Copyright 2017 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 "content/renderer/media/stream/media_stream_constraints_util_audio.h"
#include <cmath>
#include <memory>
#include <string>
#include <utility>
#include "base/message_loop/message_loop.h"
#include "content/renderer/media/stream/local_media_stream_audio_source.h"
#include "content/renderer/media/stream/media_stream_audio_source.h"
#include "content/renderer/media/stream/media_stream_source.h"
#include "content/renderer/media/stream/mock_constraint_factory.h"
#include "content/renderer/media/stream/processed_local_audio_source.h"
#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
#include "media/base/audio_parameters.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/mediastream/media_devices.mojom.h"
#include "third_party/blink/public/platform/web_media_constraints.h"
#include "third_party/blink/public/platform/web_string.h"
namespace content {
using EchoCancellationType = AudioProcessingProperties::EchoCancellationType;
namespace {
using BoolSetFunction = void (blink::BooleanConstraint::*)(bool);
using StringSetFunction =
void (blink::StringConstraint::*)(const blink::WebString&);
using MockFactoryAccessor =
blink::WebMediaTrackConstraintSet& (MockConstraintFactory::*)();
const BoolSetFunction kBoolSetFunctions[] = {
&blink::BooleanConstraint::SetExact, &blink::BooleanConstraint::SetIdeal,
};
const StringSetFunction kStringSetFunctions[] = {
&blink::StringConstraint::SetExact, &blink::StringConstraint::SetIdeal,
};
const MockFactoryAccessor kFactoryAccessors[] = {
&MockConstraintFactory::basic, &MockConstraintFactory::AddAdvanced};
const bool kBoolValues[] = {true, false};
using AudioSettingsBoolMembers =
std::vector<bool (AudioCaptureSettings::*)() const>;
using AudioPropertiesBoolMembers =
std::vector<bool AudioProcessingProperties::*>;
template <typename T>
static bool Contains(const std::vector<T>& vector, T value) {
auto it = std::find(vector.begin(), vector.end(), value);
return it != vector.end();
}
} // namespace
// This class is only used for setting |override_aec3_|. We simply inject the
// current task runner since they are not used.
class AecDumpMessageFilterForTest : public AecDumpMessageFilter {
public:
AecDumpMessageFilterForTest()
: AecDumpMessageFilter(base::MessageLoopCurrent::Get()->task_runner(),
base::MessageLoopCurrent::Get()->task_runner()) {}
void set_override_aec3(base::Optional<bool> override_aec3) {
override_aec3_ = override_aec3;
}
protected:
~AecDumpMessageFilterForTest() override {}
};
class MediaStreamConstraintsUtilAudioTest
: public testing::TestWithParam<std::string> {
public:
void SetUp() override {
ResetFactory();
if (IsDeviceCapture()) {
capabilities_.emplace_back(
"default_device", "fake_group1",
media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
media::AudioParameters::kAudioCDSampleRate,
1000));
media::AudioParameters system_echo_canceller_parameters(
media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
media::AudioParameters::kAudioCDSampleRate, 1000);
system_echo_canceller_parameters.set_effects(
media::AudioParameters::ECHO_CANCELLER);
capabilities_.emplace_back("system_echo_canceller_device", "fake_group2",
system_echo_canceller_parameters);
default_device_ = &capabilities_[0];
system_echo_canceller_device_ = &capabilities_[1];
} else {
// For content capture, use a single capability that admits all possible
// settings.
capabilities_.emplace_back();
}
}
protected:
void MakeSystemEchoCancellerDeviceExperimental() {
media::AudioParameters experimental_system_echo_canceller_parameters(
media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
media::AudioParameters::kAudioCDSampleRate, 1000);
experimental_system_echo_canceller_parameters.set_effects(
media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
capabilities_[1] = {"experimental_system_echo_canceller_device",
"fake_group3",
experimental_system_echo_canceller_parameters};
}
void SetMediaStreamSource(const std::string& source) {}
void ResetFactory() {
constraint_factory_.Reset();
constraint_factory_.basic().media_stream_source.SetExact(
blink::WebString::FromASCII(GetParam()));
}
std::string GetMediaStreamSource() { return GetParam(); }
bool IsDeviceCapture() { return GetMediaStreamSource().empty(); }
MediaStreamType GetMediaStreamType() {
std::string media_source = GetMediaStreamSource();
if (media_source.empty())
return MEDIA_DEVICE_AUDIO_CAPTURE;
else if (media_source == kMediaStreamSourceTab)
return MEDIA_TAB_AUDIO_CAPTURE;
return MEDIA_DESKTOP_AUDIO_CAPTURE;
}
std::unique_ptr<ProcessedLocalAudioSource> GetProcessedLocalAudioSource(
const AudioProcessingProperties& properties,
bool hotword_enabled,
bool disable_local_echo,
bool render_to_associated_sink,
int effects) {
MediaStreamDevice device;
device.id = "processed_source";
device.type = GetMediaStreamType();
if (render_to_associated_sink)
device.matched_output_device_id = std::string("some_device_id");
device.input.set_effects(effects);
return std::make_unique<ProcessedLocalAudioSource>(
-1, device, hotword_enabled, disable_local_echo, properties,
MediaStreamSource::ConstraintsCallback(), &pc_factory_);
}
std::unique_ptr<ProcessedLocalAudioSource> GetProcessedLocalAudioSource(
const AudioProcessingProperties& properties,
bool hotword_enabled,
bool disable_local_echo,
bool render_to_associated_sink) {
return GetProcessedLocalAudioSource(
properties, hotword_enabled, disable_local_echo,
render_to_associated_sink,
media::AudioParameters::PlatformEffectsMask::NO_EFFECTS);
}
std::unique_ptr<LocalMediaStreamAudioSource> GetLocalMediaStreamAudioSource(
bool enable_system_echo_canceller,
bool hotword_enabled,
bool disable_local_echo,
bool render_to_associated_sink) {
MediaStreamDevice device;
device.type = GetMediaStreamType();
if (enable_system_echo_canceller)
device.input.set_effects(media::AudioParameters::ECHO_CANCELLER);
if (render_to_associated_sink)
device.matched_output_device_id = std::string("some_device_id");
return std::make_unique<LocalMediaStreamAudioSource>(
-1, device, hotword_enabled, disable_local_echo,
MediaStreamSource::ConstraintsCallback());
}
AudioCaptureSettings SelectSettings() {
blink::WebMediaConstraints constraints =
constraint_factory_.CreateWebMediaConstraints();
return SelectSettingsAudioCapture(capabilities_, constraints, false);
}
// When googExperimentalEchoCancellation is not explicitly set, its default
// value is always false on Android. On other platforms it behaves like other
// audio-processing properties.
void CheckGoogExperimentalEchoCancellationDefault(
const AudioProcessingProperties& properties,
bool value) {
#if defined(OS_ANDROID)
EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
#else
EXPECT_EQ(value, properties.goog_experimental_echo_cancellation);
#endif
}
void CheckBoolDefaultsDeviceCapture(
const AudioSettingsBoolMembers& exclude_main_settings,
const AudioPropertiesBoolMembers& exclude_audio_properties,
const AudioCaptureSettings& result) {
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::hotword_enabled)) {
EXPECT_FALSE(result.hotword_enabled());
}
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::disable_local_echo)) {
EXPECT_TRUE(result.disable_local_echo());
}
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::render_to_associated_sink)) {
EXPECT_FALSE(result.render_to_associated_sink());
}
const auto& properties = result.audio_processing_properties();
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_audio_mirroring)) {
EXPECT_FALSE(properties.goog_audio_mirroring);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_auto_gain_control)) {
EXPECT_TRUE(properties.goog_auto_gain_control);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_echo_cancellation)) {
CheckGoogExperimentalEchoCancellationDefault(properties, true);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_typing_noise_detection)) {
EXPECT_TRUE(properties.goog_typing_noise_detection);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_noise_suppression)) {
EXPECT_TRUE(properties.goog_noise_suppression);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_noise_suppression)) {
EXPECT_TRUE(properties.goog_experimental_noise_suppression);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_highpass_filter)) {
EXPECT_TRUE(properties.goog_highpass_filter);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_auto_gain_control)) {
EXPECT_TRUE(properties.goog_experimental_auto_gain_control);
}
}
void CheckBoolDefaultsContentCapture(
const AudioSettingsBoolMembers& exclude_main_settings,
const AudioPropertiesBoolMembers& exclude_audio_properties,
const AudioCaptureSettings& result) {
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::hotword_enabled)) {
EXPECT_FALSE(result.hotword_enabled());
}
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::disable_local_echo)) {
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
}
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::render_to_associated_sink)) {
EXPECT_FALSE(result.render_to_associated_sink());
}
const auto& properties = result.audio_processing_properties();
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_audio_mirroring)) {
EXPECT_FALSE(properties.goog_audio_mirroring);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_auto_gain_control)) {
EXPECT_FALSE(properties.goog_auto_gain_control);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_echo_cancellation)) {
EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_typing_noise_detection)) {
EXPECT_FALSE(properties.goog_typing_noise_detection);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_noise_suppression)) {
EXPECT_FALSE(properties.goog_noise_suppression);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_noise_suppression)) {
EXPECT_FALSE(properties.goog_experimental_noise_suppression);
}
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_highpass_filter)) {
EXPECT_FALSE(properties.goog_highpass_filter);
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_auto_gain_control)) {
EXPECT_FALSE(properties.goog_experimental_auto_gain_control);
}
}
void CheckBoolDefaults(
const AudioSettingsBoolMembers& exclude_main_settings,
const AudioPropertiesBoolMembers& exclude_audio_properties,
const AudioCaptureSettings& result) {
if (IsDeviceCapture()) {
CheckBoolDefaultsDeviceCapture(exclude_main_settings,
exclude_audio_properties, result);
} else {
CheckBoolDefaultsContentCapture(exclude_main_settings,
exclude_audio_properties, result);
}
}
void CheckEchoCancellationTypeDefault(const AudioCaptureSettings& result) {
const auto& properties = result.audio_processing_properties();
if (IsDeviceCapture()) {
EXPECT_EQ(properties.echo_cancellation_type,
EchoCancellationType::kEchoCancellationAec2);
} else {
EXPECT_EQ(properties.echo_cancellation_type,
EchoCancellationType::kEchoCancellationDisabled);
}
}
void CheckDevice(const AudioDeviceCaptureCapability& expected_device,
const AudioCaptureSettings& result) {
EXPECT_EQ(expected_device.DeviceID(), result.device_id());
EXPECT_EQ(expected_device.Parameters().sample_rate(),
result.device_parameters().sample_rate());
EXPECT_EQ(expected_device.Parameters().channels(),
result.device_parameters().channels());
EXPECT_EQ(expected_device.Parameters().effects(),
result.device_parameters().effects());
}
void CheckDeviceDefaults(const AudioCaptureSettings& result) {
if (IsDeviceCapture())
CheckDevice(*default_device_, result);
else
EXPECT_TRUE(result.device_id().empty());
}
void CheckAllDefaults(
const AudioSettingsBoolMembers& exclude_main_settings,
const AudioPropertiesBoolMembers& exclude_audio_properties,
const AudioCaptureSettings& result) {
CheckBoolDefaults(exclude_main_settings, exclude_audio_properties, result);
CheckEchoCancellationTypeDefault(result);
CheckDeviceDefaults(result);
}
// Assumes that echoCancellation is set to true as a basic, exact constraint.
void CheckAudioProcessingPropertiesForExactEchoCancellationType(
const blink::WebString& echo_cancellation_type_constraint,
const AudioCaptureSettings& result) {
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// With device capture, the echo_cancellation constraint
// enables/disables all audio processing by default.
// With content capture, the echo_cancellation constraint controls
// only the echo_cancellation properties. The other audio processing
// properties default to false.
const EchoCancellationType expected_ec_type =
GetEchoCancellationTypeFromConstraintString(
echo_cancellation_type_constraint);
if (!IsDeviceCapture()) {
ASSERT_NE(EchoCancellationType::kEchoCancellationSystem,
expected_ec_type);
}
EXPECT_EQ(expected_ec_type, properties.echo_cancellation_type);
const bool enable_webrtc_audio_processing = IsDeviceCapture();
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_auto_gain_control);
CheckGoogExperimentalEchoCancellationDefault(
properties, enable_webrtc_audio_processing);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_typing_noise_detection);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_noise_suppression);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_experimental_noise_suppression);
EXPECT_EQ(enable_webrtc_audio_processing, properties.goog_highpass_filter);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_experimental_auto_gain_control);
// The following are not audio processing.
EXPECT_FALSE(properties.goog_audio_mirroring);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
if (IsDeviceCapture()) {
CheckDevice(
expected_ec_type == EchoCancellationType::kEchoCancellationSystem
? *system_echo_canceller_device_
: *default_device_,
result);
} else {
EXPECT_TRUE(result.device_id().empty());
}
}
void CheckAudioProcessingPropertiesForIdealEchoCancellationType(
const AudioCaptureSettings& result) {
const AudioProcessingProperties& properties =
result.audio_processing_properties();
EXPECT_EQ(EchoCancellationType::kEchoCancellationSystem,
properties.echo_cancellation_type);
EXPECT_TRUE(properties.goog_auto_gain_control);
CheckGoogExperimentalEchoCancellationDefault(properties, true);
EXPECT_TRUE(properties.goog_typing_noise_detection);
EXPECT_TRUE(properties.goog_noise_suppression);
EXPECT_TRUE(properties.goog_experimental_noise_suppression);
EXPECT_TRUE(properties.goog_highpass_filter);
EXPECT_TRUE(properties.goog_experimental_auto_gain_control);
// The following are not audio processing.
EXPECT_FALSE(properties.goog_audio_mirroring);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
CheckDevice(*system_echo_canceller_device_, result);
}
EchoCancellationType GetEchoCancellationTypeFromConstraintString(
const blink::WebString& constraint_string) {
if (constraint_string == kEchoCancellationTypeValues[0])
return EchoCancellationType::kEchoCancellationAec2;
if (constraint_string == kEchoCancellationTypeValues[1])
return EchoCancellationType::kEchoCancellationAec3;
if (constraint_string == kEchoCancellationTypeValues[2])
return EchoCancellationType::kEchoCancellationSystem;
ADD_FAILURE() << "Invalid echo cancellation type constraint: "
<< constraint_string.Ascii();
return EchoCancellationType::kEchoCancellationDisabled;
}
MockConstraintFactory constraint_factory_;
AudioDeviceCaptureCapabilities capabilities_;
const AudioDeviceCaptureCapability* default_device_ = nullptr;
const AudioDeviceCaptureCapability* system_echo_canceller_device_ = nullptr;
const std::vector<media::Point> kMicPositions = {{8, 8, 8}, {4, 4, 4}};
// TODO(grunell): Store these as separate constants and compare against those
// in tests, instead of indexing the vector.
const std::vector<blink::WebString> kEchoCancellationTypeValues = {
blink::WebString::FromASCII("browser"),
blink::WebString::FromASCII("aec3"),
blink::WebString::FromASCII("system")};
private:
// Required for tests involving a MediaStreamAudioSource.
base::MessageLoop message_loop_;
MockPeerConnectionDependencyFactory pc_factory_;
};
// The Unconstrained test checks the default selection criteria.
TEST_P(MediaStreamConstraintsUtilAudioTest, Unconstrained) {
auto result = SelectSettings();
// All settings should have default values.
EXPECT_TRUE(result.HasValue());
CheckAllDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
}
// This test checks all possible ways to set boolean constraints (except
// echo cancellation constraints, which are not mapped 1:1 to output audio
// processing properties).
TEST_P(MediaStreamConstraintsUtilAudioTest, SingleBoolConstraint) {
// TODO(crbug.com/736309): Use braced initialization instead of push_back once
// clang has been fixed.
AudioSettingsBoolMembers kMainSettings;
kMainSettings.push_back(&AudioCaptureSettings::hotword_enabled);
kMainSettings.push_back(&AudioCaptureSettings::disable_local_echo);
kMainSettings.push_back(&AudioCaptureSettings::render_to_associated_sink);
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kMainBoolConstraints = {
&blink::WebMediaTrackConstraintSet::hotword_enabled,
&blink::WebMediaTrackConstraintSet::disable_local_echo,
&blink::WebMediaTrackConstraintSet::render_to_associated_sink};
ASSERT_EQ(kMainSettings.size(), kMainBoolConstraints.size());
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (size_t i = 0; i < kMainSettings.size(); ++i) {
for (bool value : kBoolValues) {
ResetFactory();
(((constraint_factory_.*accessor)().*kMainBoolConstraints[i]).*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(value, (result.*kMainSettings[i])());
CheckAllDefaults({kMainSettings[i]}, AudioPropertiesBoolMembers(),
result);
}
}
}
}
const AudioPropertiesBoolMembers kAudioProcessingProperties = {
&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_auto_gain_control,
&AudioProcessingProperties::goog_experimental_echo_cancellation,
&AudioProcessingProperties::goog_typing_noise_detection,
&AudioProcessingProperties::goog_noise_suppression,
&AudioProcessingProperties::goog_experimental_noise_suppression,
&AudioProcessingProperties::goog_highpass_filter,
&AudioProcessingProperties::goog_experimental_auto_gain_control};
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kAudioProcessingConstraints = {
&blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
&blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
&blink::WebMediaTrackConstraintSet::
goog_experimental_echo_cancellation,
&blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
&blink::WebMediaTrackConstraintSet::goog_noise_suppression,
&blink::WebMediaTrackConstraintSet::
goog_experimental_noise_suppression,
&blink::WebMediaTrackConstraintSet::goog_highpass_filter,
&blink::WebMediaTrackConstraintSet::
goog_experimental_auto_gain_control,
};
ASSERT_EQ(kAudioProcessingProperties.size(),
kAudioProcessingConstraints.size());
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (size_t i = 0; i < kAudioProcessingProperties.size(); ++i) {
for (bool value : kBoolValues) {
ResetFactory();
(((constraint_factory_.*accessor)().*kAudioProcessingConstraints[i]).*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(value, result.audio_processing_properties().*
kAudioProcessingProperties[i]);
CheckAllDefaults(AudioSettingsBoolMembers(),
{kAudioProcessingProperties[i]}, result);
}
}
}
}
}
// DeviceID tests.
TEST_P(MediaStreamConstraintsUtilAudioTest, ExactArbitraryDeviceID) {
const std::string kArbitraryDeviceID = "arbitrary";
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(kArbitraryDeviceID));
auto result = SelectSettings();
// kArbitraryDeviceID is invalid for device capture, but it is considered
// valid for content capture. For content capture, validation of device
// capture is performed by the getUserMedia() implementation.
if (IsDeviceCapture()) {
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(std::string(constraint_factory_.basic().device_id.GetName()),
std::string(result.failed_constraint_name()));
} else {
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(kArbitraryDeviceID, result.device_id());
CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
CheckEchoCancellationTypeDefault(result);
}
}
// DeviceID tests check various ways to deal with the device_id constraint.
TEST_P(MediaStreamConstraintsUtilAudioTest, IdealArbitraryDeviceID) {
const std::string kArbitraryDeviceID = "arbitrary";
constraint_factory_.basic().device_id.SetIdeal(
blink::WebString::FromASCII(kArbitraryDeviceID));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// kArbitraryDeviceID is invalid for device capture, but it is considered
// valid for content capture. For content capture, validation of device
// capture is performed by the getUserMedia() implementation.
if (IsDeviceCapture())
CheckDeviceDefaults(result);
else
EXPECT_EQ(kArbitraryDeviceID, result.device_id());
CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
CheckEchoCancellationTypeDefault(result);
}
TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidDeviceID) {
for (const auto& device : capabilities_) {
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(device.DeviceID()));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDevice(device, result);
CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
EchoCancellationType expected_echo_cancellation_type =
EchoCancellationType::kEchoCancellationDisabled;
if (IsDeviceCapture()) {
const bool has_system_echo_cancellation =
device.Parameters().effects() &
media::AudioParameters::ECHO_CANCELLER;
expected_echo_cancellation_type =
has_system_echo_cancellation
? EchoCancellationType::kEchoCancellationSystem
: EchoCancellationType::kEchoCancellationAec2;
}
EXPECT_EQ(expected_echo_cancellation_type,
result.audio_processing_properties().echo_cancellation_type);
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, ExactGroupID) {
for (const auto& device : capabilities_) {
constraint_factory_.basic().group_id.SetExact(
blink::WebString::FromASCII(device.GroupID()));
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDevice(device, result);
CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
EchoCancellationType expected_echo_cancellation_type =
EchoCancellationType::kEchoCancellationDisabled;
if (IsDeviceCapture()) {
const bool has_system_echo_cancellation =
device.Parameters().effects() &
media::AudioParameters::ECHO_CANCELLER;
expected_echo_cancellation_type =
has_system_echo_cancellation
? EchoCancellationType::kEchoCancellationSystem
: EchoCancellationType::kEchoCancellationAec2;
}
EXPECT_EQ(expected_echo_cancellation_type,
result.audio_processing_properties().echo_cancellation_type);
}
}
// Tests the echoCancellation constraint with a device without system echo
// cancellation.
TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithWebRtc) {
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (bool value : kBoolValues) {
ResetFactory();
((constraint_factory_.*accessor)().echo_cancellation.*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// With device capture, the echo_cancellation constraint
// enables/disables all audio processing by default.
// With content capture, the echo_cancellation constraint controls
// only the echo_cancellation properties. The other audio processing
// properties default to false.
const EchoCancellationType expected_echo_cancellation_type =
value ? EchoCancellationType::kEchoCancellationAec2
: EchoCancellationType::kEchoCancellationDisabled;
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
const bool enable_webrtc_audio_processing =
IsDeviceCapture() ? value : false;
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_auto_gain_control);
CheckGoogExperimentalEchoCancellationDefault(
properties, enable_webrtc_audio_processing);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_typing_noise_detection);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_noise_suppression);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_experimental_noise_suppression);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_highpass_filter);
EXPECT_EQ(enable_webrtc_audio_processing,
properties.goog_experimental_auto_gain_control);
// The following are not audio processing.
EXPECT_FALSE(properties.goog_audio_mirroring);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
if (IsDeviceCapture()) {
CheckDevice(*default_device_, result);
} else {
EXPECT_TRUE(result.device_id().empty());
}
}
}
}
}
// Tests the echoCancellation constraint with a device with system echo
// cancellation.
TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithSystem) {
// With content capture, there is no system echo cancellation, so
// nothing to test.
if (!IsDeviceCapture())
return;
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (bool value : kBoolValues) {
ResetFactory();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(
system_echo_canceller_device_->DeviceID()));
((constraint_factory_.*accessor)().echo_cancellation.*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// With system echo cancellation, the echo_cancellation constraint
// enables/disables all audio processing by default, WebRTC echo
// cancellation is always disabled, and system echo cancellation is
// disabled if the echo_cancellation constraint is false.
const EchoCancellationType expected_echo_cancellation_type =
value ? EchoCancellationType::kEchoCancellationSystem
: EchoCancellationType::kEchoCancellationDisabled;
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
EXPECT_EQ(value, properties.goog_auto_gain_control);
CheckGoogExperimentalEchoCancellationDefault(properties, value);
EXPECT_EQ(value, properties.goog_typing_noise_detection);
EXPECT_EQ(value, properties.goog_noise_suppression);
EXPECT_EQ(value, properties.goog_experimental_noise_suppression);
EXPECT_EQ(value, properties.goog_highpass_filter);
EXPECT_EQ(value, properties.goog_experimental_auto_gain_control);
// The following are not audio processing.
EXPECT_FALSE(properties.goog_audio_mirroring);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
CheckDevice(*system_echo_canceller_device_, result);
}
}
}
}
// Tests the googEchoCancellation constraint with a device without system echo
// cancellation.
TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithWebRtc) {
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointers due to the comparison failing
// on compilers.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (bool value : kBoolValues) {
ResetFactory();
((constraint_factory_.*accessor)().goog_echo_cancellation.*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// The goog_echo_cancellation constraint controls only the
// echo_cancellation properties. The other audio processing properties
// use the default values.
const EchoCancellationType expected_echo_cancellation_type =
value ? EchoCancellationType::kEchoCancellationAec2
: EchoCancellationType::kEchoCancellationDisabled;
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
CheckBoolDefaults(AudioSettingsBoolMembers(),
AudioPropertiesBoolMembers(), result);
if (IsDeviceCapture()) {
CheckDevice(*default_device_, result);
} else {
EXPECT_TRUE(result.device_id().empty());
}
}
}
}
}
// Tests the googEchoCancellation constraint with a device with system echo
// cancellation.
TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithSystem) {
// With content capture, there is no system echo cancellation, so
// nothing to test.
if (!IsDeviceCapture())
return;
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (bool value : kBoolValues) {
ResetFactory();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(
system_echo_canceller_device_->DeviceID()));
((constraint_factory_.*accessor)().goog_echo_cancellation.*
set_function)(value);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// With system echo cancellation, WebRTC echo cancellation is always
// disabled, and system echo cancellation is disabled if
// goog_echo_cancellation is false.
const EchoCancellationType expected_echo_cancellation_type =
value ? EchoCancellationType::kEchoCancellationSystem
: EchoCancellationType::kEchoCancellationDisabled;
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
CheckBoolDefaults(AudioSettingsBoolMembers(),
AudioPropertiesBoolMembers(), result);
CheckDevice(*system_echo_canceller_device_, result);
}
}
}
}
// Tests the echoCancellationType constraint without constraining to a device
// with system echo cancellation. Tested as basic exact constraints.
TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationTypeExact) {
for (blink::WebString value : kEchoCancellationTypeValues) {
ResetFactory();
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetExact(value);
auto result = SelectSettings();
// If content capture and EC type "system", we expect failure.
if (!IsDeviceCapture() && value == kEchoCancellationTypeValues[2]) {
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(result.failed_constraint_name(),
constraint_factory_.basic().echo_cancellation_type.GetName());
continue;
}
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForExactEchoCancellationType(value, result);
}
}
// Like the test above, but changes the device with system echo cancellation
// support to only support experimental system echo cancellation. It should
// still be picked if requested.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeExact_Experimental) {
if (!IsDeviceCapture())
return;
// Replace the device with one that only supports experimental system echo
// cancellation.
MakeSystemEchoCancellerDeviceExperimental();
for (blink::WebString value : kEchoCancellationTypeValues) {
ResetFactory();
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetExact(value);
auto result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForExactEchoCancellationType(value, result);
}
}
// Tests the echoCancellationType constraint without constraining to a device
// with system echo cancellation. Tested as basic ideal constraints.
TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationTypeIdeal) {
// With content capture, there is no system echo cancellation, so
// nothing to test.
if (!IsDeviceCapture())
return;
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetIdeal(
kEchoCancellationTypeValues[2]);
auto result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForIdealEchoCancellationType(result);
}
// Like the test above, but only having a device with experimental system echo
// cancellation available.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeIdeal_Experimental) {
// With content capture, there is no system echo cancellation, so
// nothing to test.
if (!IsDeviceCapture())
return;
// Replace the device with one that only supports experimental system echo
// cancellation.
MakeSystemEchoCancellerDeviceExperimental();
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetIdeal(
kEchoCancellationTypeValues[2]);
auto result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForIdealEchoCancellationType(result);
}
// Tests the echoCancellationType constraint with constraining to a device with
// experimental system echo cancellation.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeWithExpSystemDeviceConstraint) {
// With content capture, there is no system echo cancellation, so
// nothing to test.
if (!IsDeviceCapture())
return;
MakeSystemEchoCancellerDeviceExperimental();
// Include leaving the echoCancellationType constraint unset in the tests.
// It should then behave as before the constraint was introduced.
auto echo_cancellation_types_and_unset = kEchoCancellationTypeValues;
echo_cancellation_types_and_unset.push_back(blink::WebString());
for (auto set_function : kStringSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kStringSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (blink::WebString ec_type_value : echo_cancellation_types_and_unset) {
for (bool ec_value : kBoolValues) {
ResetFactory();
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(
system_echo_canceller_device_->DeviceID()));
constraint_factory_.basic().echo_cancellation.SetExact(ec_value);
if (!ec_type_value.IsNull())
((constraint_factory_.*accessor)().echo_cancellation_type.*
set_function)(ec_type_value);
// We should get a result if echo cancellation is enabled or if it's
// disabled and we set the type as an advanced or ideal constraint, or
// we've left the constraint unset.
auto result = SelectSettings();
const bool advanced_constraint = accessor == kFactoryAccessors[1];
const bool ideal_constraint = set_function == kStringSetFunctions[1];
const bool should_have_result = ec_value || advanced_constraint ||
ideal_constraint ||
ec_type_value.IsNull();
EXPECT_EQ(should_have_result, result.HasValue());
if (!should_have_result)
continue;
const AudioProcessingProperties& properties =
result.audio_processing_properties();
// With experimental system echo cancellation (echo canceller type
// "system"), the echo_cancellation constraint enables/disables all
// audio processing by default, WebRTC echo cancellation is always
// disabled, and experimental system echo cancellation is disabled
// if the echo_cancellation constraint is false.
if (ec_value) {
const EchoCancellationType expected_echo_cancellation_type =
ec_type_value == blink::WebString()
? EchoCancellationType::kEchoCancellationAec2
: GetEchoCancellationTypeFromConstraintString(
ec_type_value);
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
} else {
EXPECT_EQ(EchoCancellationType::kEchoCancellationDisabled,
properties.echo_cancellation_type);
}
EXPECT_EQ(ec_value, properties.goog_auto_gain_control);
CheckGoogExperimentalEchoCancellationDefault(properties, ec_value);
EXPECT_EQ(ec_value, properties.goog_typing_noise_detection);
EXPECT_EQ(ec_value, properties.goog_noise_suppression);
EXPECT_EQ(ec_value, properties.goog_experimental_noise_suppression);
EXPECT_EQ(ec_value, properties.goog_highpass_filter);
EXPECT_EQ(ec_value, properties.goog_experimental_auto_gain_control);
// The following are not audio processing.
EXPECT_FALSE(properties.goog_audio_mirroring);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_EQ(GetMediaStreamSource() != kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
CheckDevice(*system_echo_canceller_device_, result);
}
}
}
}
}
// Tests the echoCancellationType constraint with constraining to a device
// without experimental system echo cancellation, which should fail.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeWithWebRtcDeviceConstraint) {
if (!IsDeviceCapture())
return;
constraint_factory_.basic().device_id.SetExact(
blink::WebString::FromASCII(default_device_->DeviceID()));
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetExact(
kEchoCancellationTypeValues[2]);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(result.failed_constraint_name(),
constraint_factory_.basic().device_id.GetName());
}
// Tests the echoCancellationType constraint when also the AEC3 has been
// selected via the extension API. That selection ends up in
// AecDumpMessageFilter and MediaStreamConstraintsUtil checks there if set and
// to what. It can be unset, true or false. The constraint has precedence over
// the extension API selection. Tested as basic exact constraints.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeAndAec3Selection) {
// First test AEC3 selection when no echo cancellation type constraint has
// been set.
scoped_refptr<AecDumpMessageFilterForTest> admf =
new AecDumpMessageFilterForTest();
admf->set_override_aec3(true);
ResetFactory();
constraint_factory_.basic().echo_cancellation.SetExact(true);
auto result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForExactEchoCancellationType(
kEchoCancellationTypeValues[1], // AEC3
result);
// Set the echo cancellation type constraint to browser and expect that as
// result.
ResetFactory();
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetExact(
kEchoCancellationTypeValues[0]);
result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForExactEchoCancellationType(
kEchoCancellationTypeValues[0], // Browser
result);
// Set the AEC3 selection to false and echo cancellation type constraint to
// AEC3 and expect AEC3 as result.
admf->set_override_aec3(false);
ResetFactory();
constraint_factory_.basic().echo_cancellation.SetExact(true);
constraint_factory_.basic().echo_cancellation_type.SetExact(
kEchoCancellationTypeValues[1]);
result = SelectSettings();
ASSERT_TRUE(result.HasValue());
CheckAudioProcessingPropertiesForExactEchoCancellationType(
kEchoCancellationTypeValues[1], // AEC3
result);
}
// Test that having differing mandatory values for echoCancellation and
// googEchoCancellation fails.
TEST_P(MediaStreamConstraintsUtilAudioTest, ContradictoryEchoCancellation) {
for (bool value : kBoolValues) {
constraint_factory_.basic().echo_cancellation.SetExact(value);
constraint_factory_.basic().goog_echo_cancellation.SetExact(!value);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(result.failed_constraint_name(),
constraint_factory_.basic().echo_cancellation.GetName());
}
}
// Tests that individual boolean audio-processing constraints override the
// default value set by the echoCancellation constraint.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationAndSingleBoolConstraint) {
const AudioPropertiesBoolMembers kAudioProcessingProperties = {
&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_auto_gain_control,
&AudioProcessingProperties::goog_experimental_echo_cancellation,
&AudioProcessingProperties::goog_typing_noise_detection,
&AudioProcessingProperties::goog_noise_suppression,
&AudioProcessingProperties::goog_experimental_noise_suppression,
&AudioProcessingProperties::goog_highpass_filter,
&AudioProcessingProperties::goog_experimental_auto_gain_control};
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kAudioProcessingConstraints = {
&blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
&blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
&blink::WebMediaTrackConstraintSet::
goog_experimental_echo_cancellation,
&blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
&blink::WebMediaTrackConstraintSet::goog_noise_suppression,
&blink::WebMediaTrackConstraintSet::
goog_experimental_noise_suppression,
&blink::WebMediaTrackConstraintSet::goog_highpass_filter,
&blink::WebMediaTrackConstraintSet::
goog_experimental_auto_gain_control,
};
ASSERT_EQ(kAudioProcessingProperties.size(),
kAudioProcessingConstraints.size());
for (auto set_function : kBoolSetFunctions) {
for (auto accessor : kFactoryAccessors) {
// Ideal advanced is ignored by the SelectSettings algorithm.
// Using array elements instead of pointer values due to the comparison
// failing on some build configurations.
if (set_function == kBoolSetFunctions[1] &&
accessor == kFactoryAccessors[1]) {
continue;
}
for (size_t i = 0; i < kAudioProcessingProperties.size(); ++i) {
ResetFactory();
((constraint_factory_.*accessor)().echo_cancellation.*
set_function)(false);
(((constraint_factory_.*accessor)().*kAudioProcessingConstraints[i]).*
set_function)(true);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(EchoCancellationType::kEchoCancellationDisabled,
result.audio_processing_properties().echo_cancellation_type);
EXPECT_TRUE(result.audio_processing_properties().*
kAudioProcessingProperties[i]);
for (size_t j = 0; j < kAudioProcessingProperties.size(); ++j) {
if (i == j)
continue;
EXPECT_FALSE(result.audio_processing_properties().*
kAudioProcessingProperties[j]);
}
}
}
}
}
// Test advanced constraints sets that can be satisfied.
TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedCompatibleConstraints) {
constraint_factory_.AddAdvanced().render_to_associated_sink.SetExact(true);
constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDeviceDefaults(result);
// TODO(crbug.com/736309): Fold this local when clang is fixed.
auto render_to_associated_sink =
&AudioCaptureSettings::render_to_associated_sink;
CheckBoolDefaults({render_to_associated_sink},
{&AudioProcessingProperties::goog_audio_mirroring}, result);
CheckEchoCancellationTypeDefault(result);
EXPECT_TRUE(result.render_to_associated_sink());
EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
}
// Test that an advanced constraint set that contradicts a previous constraint
// set is ignored, but that further constraint sets that can be satisfied are
// applied.
TEST_P(MediaStreamConstraintsUtilAudioTest,
AdvancedConflictingMiddleConstraints) {
constraint_factory_.AddAdvanced().goog_highpass_filter.SetExact(true);
auto& advanced2 = constraint_factory_.AddAdvanced();
advanced2.goog_highpass_filter.SetExact(false);
advanced2.hotword_enabled.SetExact(true);
constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDeviceDefaults(result);
EXPECT_FALSE(result.hotword_enabled());
// TODO(crbug.com/736309): Fold this local when clang is fixed.
auto hotword_enabled = &AudioCaptureSettings::hotword_enabled;
CheckBoolDefaults({hotword_enabled},
{&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_highpass_filter},
result);
CheckEchoCancellationTypeDefault(result);
EXPECT_FALSE(result.hotword_enabled());
EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
EXPECT_TRUE(result.audio_processing_properties().goog_highpass_filter);
}
// Test that an advanced constraint set that contradicts a previous constraint
// set with a boolean constraint is ignored.
TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedConflictingLastConstraint) {
constraint_factory_.AddAdvanced().goog_highpass_filter.SetExact(true);
constraint_factory_.AddAdvanced().hotword_enabled.SetExact(true);
constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
constraint_factory_.AddAdvanced().hotword_enabled.SetExact(false);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDeviceDefaults(result);
// TODO(crbug.com/736309): Fold this local when clang is fixed.
auto hotword_enabled = &AudioCaptureSettings::hotword_enabled;
CheckBoolDefaults({hotword_enabled},
{&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_highpass_filter},
result);
CheckEchoCancellationTypeDefault(result);
// The fourth advanced set is ignored because it contradicts the second set.
EXPECT_TRUE(result.hotword_enabled());
EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
EXPECT_TRUE(result.audio_processing_properties().goog_highpass_filter);
}
// NoDevices tests verify that the case with no devices is handled correctly.
TEST_P(MediaStreamConstraintsUtilAudioTest, NoDevicesNoConstraints) {
// This test makes sense only for device capture.
if (!IsDeviceCapture())
return;
AudioDeviceCaptureCapabilities capabilities;
auto result = SelectSettingsAudioCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints(), false);
EXPECT_FALSE(result.HasValue());
EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
}
TEST_P(MediaStreamConstraintsUtilAudioTest, NoDevicesWithConstraints) {
// This test makes sense only for device capture.
if (!IsDeviceCapture())
return;
AudioDeviceCaptureCapabilities capabilities;
constraint_factory_.basic().sample_size.SetExact(16);
auto result = SelectSettingsAudioCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints(), false);
EXPECT_FALSE(result.HasValue());
EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
}
// Test functionality to support applyConstraints() for tracks attached to
// sources that have no audio processing.
TEST_P(MediaStreamConstraintsUtilAudioTest, SourceWithNoAudioProcessing) {
for (bool enable_properties : {true, false}) {
std::unique_ptr<LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(
enable_properties /* enable_system_echo_canceller */,
enable_properties /* hotword_enabled */,
enable_properties /* disable_local_echo */,
enable_properties /* render_to_associated_sink */);
// These constraints are false in |source|.
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kConstraints = {
&blink::WebMediaTrackConstraintSet::echo_cancellation,
&blink::WebMediaTrackConstraintSet::hotword_enabled,
&blink::WebMediaTrackConstraintSet::disable_local_echo,
&blink::WebMediaTrackConstraintSet::render_to_associated_sink,
};
for (size_t i = 0; i < kConstraints.size(); ++i) {
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i])
.SetExact(enable_properties);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i])
.SetExact(!enable_properties);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Setting just ideal values should always succeed.
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i]).SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i]).SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
}
}
// Test functionality to support applyConstraints() for echo cancellation type
// for tracks attached to sources that have no audio processing.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeWithSourceWithNoAudioProcessing) {
for (blink::WebString value : kEchoCancellationTypeValues) {
std::unique_ptr<LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
false /* hotword_enabled */,
false /* disable_local_echo */,
false /* render_to_associated_sink */);
// No echo cancellation is available so we expect failure.
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation_type.SetExact(value);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Setting just ideal values should always succeed.
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation_type.SetIdeal(value);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
}
// Test functionality to support applyConstraints() for tracks attached to
// sources that have audio processing.
TEST_P(MediaStreamConstraintsUtilAudioTest, SourceWithAudioProcessing) {
// Processed audio sources are supported only for device capture.
if (!IsDeviceCapture())
return;
for (bool use_defaults : {true, false}) {
AudioProcessingProperties properties;
if (!use_defaults) {
properties.echo_cancellation_type =
EchoCancellationType::kEchoCancellationDisabled;
properties.goog_audio_mirroring = !properties.goog_audio_mirroring;
properties.goog_auto_gain_control = !properties.goog_auto_gain_control;
properties.goog_experimental_echo_cancellation =
!properties.goog_experimental_echo_cancellation;
properties.goog_typing_noise_detection =
!properties.goog_typing_noise_detection;
properties.goog_noise_suppression = !properties.goog_noise_suppression;
properties.goog_experimental_noise_suppression =
!properties.goog_experimental_noise_suppression;
properties.goog_highpass_filter = !properties.goog_highpass_filter;
properties.goog_experimental_auto_gain_control =
!properties.goog_experimental_auto_gain_control;
}
std::unique_ptr<ProcessedLocalAudioSource> source =
GetProcessedLocalAudioSource(
properties, use_defaults /* hotword_enabled */,
use_defaults /* disable_local_echo */,
use_defaults /* render_to_associated_sink */);
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kAudioProcessingConstraints = {
&blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
&blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
&blink::WebMediaTrackConstraintSet::
goog_experimental_echo_cancellation,
&blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
&blink::WebMediaTrackConstraintSet::goog_noise_suppression,
&blink::WebMediaTrackConstraintSet::
goog_experimental_noise_suppression,
&blink::WebMediaTrackConstraintSet::goog_highpass_filter,
&blink::WebMediaTrackConstraintSet::
goog_experimental_auto_gain_control,
};
const AudioPropertiesBoolMembers kAudioProcessingProperties = {
&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_auto_gain_control,
&AudioProcessingProperties::goog_experimental_echo_cancellation,
&AudioProcessingProperties::goog_typing_noise_detection,
&AudioProcessingProperties::goog_noise_suppression,
&AudioProcessingProperties::goog_experimental_noise_suppression,
&AudioProcessingProperties::goog_highpass_filter,
&AudioProcessingProperties::goog_experimental_auto_gain_control};
ASSERT_EQ(kAudioProcessingConstraints.size(),
kAudioProcessingProperties.size());
for (size_t i = 0; i < kAudioProcessingConstraints.size(); ++i) {
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetExact(properties.*kAudioProcessingProperties[i]);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetExact(!(properties.*kAudioProcessingProperties[i]));
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Setting just ideal values should always succeed.
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
// Test same as above but for echo cancellation.
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(
properties.echo_cancellation_type ==
EchoCancellationType::kEchoCancellationAec2);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(
properties.echo_cancellation_type !=
EchoCancellationType::kEchoCancellationAec2);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
// These constraints are false in |source|.
const std::vector<
blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*>
kAudioBrowserConstraints = {
&blink::WebMediaTrackConstraintSet::hotword_enabled,
&blink::WebMediaTrackConstraintSet::disable_local_echo,
&blink::WebMediaTrackConstraintSet::render_to_associated_sink,
};
for (size_t i = 0; i < kAudioBrowserConstraints.size(); ++i) {
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i])
.SetExact(use_defaults);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i])
.SetExact(!use_defaults);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i]).SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i])
.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
// Test same as above for echo cancellation.
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(use_defaults);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(!use_defaults);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
}
// Test functionality to support applyConstraints() for echo cancellation type
// for tracks attached to sources that have audio processing.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationTypeWithSourceWithAudioProcessing) {
// Processed audio sources are supported only for device capture.
if (!IsDeviceCapture())
return;
const EchoCancellationType kEchoCancellationTypes[] = {
EchoCancellationType::kEchoCancellationDisabled,
EchoCancellationType::kEchoCancellationAec2,
EchoCancellationType::kEchoCancellationAec3,
EchoCancellationType::kEchoCancellationSystem};
for (EchoCancellationType ec_type : kEchoCancellationTypes) {
AudioProcessingProperties properties;
properties.DisableDefaultProperties();
properties.echo_cancellation_type = ec_type;
std::unique_ptr<ProcessedLocalAudioSource> source =
GetProcessedLocalAudioSource(
properties, false /* hotword_enabled */,
false /* disable_local_echo */,
false /* render_to_associated_sink */,
ec_type == EchoCancellationType::kEchoCancellationSystem
? media::AudioParameters::PlatformEffectsMask::
EXPERIMENTAL_ECHO_CANCELLER
: media::AudioParameters::PlatformEffectsMask::NO_EFFECTS);
for (blink::WebString value : kEchoCancellationTypeValues) {
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation_type.SetExact(value);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
const bool should_have_result_value =
ec_type == GetEchoCancellationTypeFromConstraintString(value);
EXPECT_EQ(should_have_result_value, result.HasValue());
// Setting just ideal values should always succeed.
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation_type.SetIdeal(value);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateWebMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, UsedAndUnusedSources) {
// The distinction of used and unused sources is relevant only for device
// capture.
if (!IsDeviceCapture())
return;
AudioProcessingProperties properties;
std::unique_ptr<ProcessedLocalAudioSource> processed_source =
GetProcessedLocalAudioSource(properties, false /* hotword_enabled */,
false /* disable_local_echo */,
false /* render_to_associated_sink */);
const std::string kUnusedDeviceID = "unused_device";
const std::string kGroupID = "fake_group";
AudioDeviceCaptureCapabilities capabilities;
capabilities.emplace_back(processed_source.get());
capabilities.emplace_back(kUnusedDeviceID, kGroupID,
media::AudioParameters::UnavailableDeviceParams());
{
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(false);
auto result = SelectSettingsAudioCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints(),
false /* should_disable_hardware_noise_suppression */);
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(result.device_id(), kUnusedDeviceID);
EXPECT_EQ(result.audio_processing_properties().echo_cancellation_type,
EchoCancellationType::kEchoCancellationDisabled);
}
{
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(true);
auto result = SelectSettingsAudioCapture(
capabilities, constraint_factory_.CreateWebMediaConstraints(),
false /* should_disable_hardware_noise_suppression */);
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(result.device_id(), processed_source->device().id);
EXPECT_EQ(result.audio_processing_properties().echo_cancellation_type,
EchoCancellationType::kEchoCancellationAec2);
}
}
INSTANTIATE_TEST_CASE_P(,
MediaStreamConstraintsUtilAudioTest,
testing::Values("",
kMediaStreamSourceTab,
kMediaStreamSourceSystem,
kMediaStreamSourceDesktop));
} // namespace content