Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(305)

Unified Diff: content/renderer/media/media_stream_constraints_util_audio_unittest.cc

Issue 2941553003: Reland "SelectSettings algorithm for audio constraints." (Closed)
Patch Set: fix test Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/media_stream_constraints_util_audio_unittest.cc
diff --git a/content/renderer/media/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/media_stream_constraints_util_audio_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..db06ed6321677159f65b2a220b77c3d303ca64a6
--- /dev/null
+++ b/content/renderer/media/media_stream_constraints_util_audio_unittest.cc
@@ -0,0 +1,1396 @@
+// 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/media_stream_constraints_util_audio.h"
+
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "content/common/media/media_devices.mojom.h"
+#include "content/renderer/media/mock_constraint_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace content {
+
+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
+
+class MediaStreamConstraintsUtilAudioTest
+ : public testing::TestWithParam<std::string> {
+ public:
+ void SetUp() override {
+ ResetFactory();
+ if (!IsDeviceCapture())
+ return;
+
+ ::mojom::AudioInputDeviceCapabilitiesPtr device =
+ ::mojom::AudioInputDeviceCapabilities::New();
+ device->device_id = "default_device";
+ device->parameters = media::AudioParameters(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO,
+ media::AudioParameters::kAudioCDSampleRate, 16, 1000);
+ capabilities_.push_back(std::move(device));
+
+ device = ::mojom::AudioInputDeviceCapabilities::New();
+ device->device_id = "mono_phone_device";
+ device->parameters = media::AudioParameters(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_MONO,
+ media::AudioParameters::kTelephoneSampleRate, 16, 1000);
+ capabilities_.push_back(std::move(device));
+
+ device = ::mojom::AudioInputDeviceCapabilities::New();
+ device->device_id = "hw_echo_canceller_device";
+ device->parameters = media::AudioParameters(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO,
+ media::AudioParameters::kAudioCDSampleRate, 24, 1000);
+ device->parameters.set_effects(media::AudioParameters::ECHO_CANCELLER);
+ capabilities_.push_back(std::move(device));
+
+ device = ::mojom::AudioInputDeviceCapabilities::New();
+ device->device_id = "octagonal_device";
+ device->parameters = media::AudioParameters(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_OCTAGONAL, 100000, 8, 1000);
+ capabilities_.push_back(std::move(device));
+
+ device = ::mojom::AudioInputDeviceCapabilities::New();
+ device->device_id = "geometry device";
+ device->parameters = media::AudioParameters(
+ media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO,
+ media::AudioParameters::kAudioCDSampleRate, 16, 1000);
+ device->parameters.set_mic_positions(kMicPositions);
+ capabilities_.push_back(std::move(device));
+
+ default_device_ = capabilities_[0].get();
+ mono_phone_device_ = capabilities_[1].get();
+ hw_echo_canceller_device_ = capabilities_[2].get();
+ octagonal_device_ = capabilities_[3].get();
+ geometry_device_ = capabilities_[4].get();
+ }
+
+ protected:
+ 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(); }
+
+ AudioCaptureSettings SelectSettings() {
+ blink::WebMediaConstraints constraints =
+ constraint_factory_.CreateWebMediaConstraints();
+ return SelectSettingsAudioCapture(capabilities_, constraints);
+ }
+
+ // 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::enable_sw_echo_cancellation)) {
+ EXPECT_TRUE(properties.enable_sw_echo_cancellation);
+ }
+ if (!Contains(exclude_audio_properties,
+ &AudioProcessingProperties::disable_hw_echo_cancellation)) {
+ EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+ }
+ 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_beamforming)) {
+ EXPECT_TRUE(properties.goog_beamforming);
+ }
+ 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::enable_sw_echo_cancellation)) {
+ EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+ }
+ if (!Contains(exclude_audio_properties,
+ &AudioProcessingProperties::disable_hw_echo_cancellation)) {
+ EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+ }
+ 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_beamforming)) {
+ EXPECT_FALSE(properties.goog_beamforming);
+ }
+ 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 CheckDevice(const mojom::AudioInputDeviceCapabilities& expected_device,
+ const AudioCaptureSettings& result) {
+ EXPECT_EQ(expected_device.device_id, result.device_id());
+ EXPECT_EQ(expected_device.parameters.sample_rate(),
+ result.device_parameters().sample_rate());
+ EXPECT_EQ(expected_device.parameters.bits_per_sample(),
+ result.device_parameters().bits_per_sample());
+ 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 CheckGeometryDefaults(const AudioCaptureSettings& result) {
+ EXPECT_TRUE(
+ result.audio_processing_properties().goog_array_geometry.empty());
+ }
+
+ void CheckAllDefaults(
+ const AudioSettingsBoolMembers& exclude_main_settings,
+ const AudioPropertiesBoolMembers& exclude_audio_properties,
+ const AudioCaptureSettings& result) {
+ CheckBoolDefaults(exclude_main_settings, exclude_audio_properties, result);
+ CheckDeviceDefaults(result);
+ CheckGeometryDefaults(result);
+ }
+
+ MockConstraintFactory constraint_factory_;
+ AudioDeviceCaptureCapabilities capabilities_;
+ const mojom::AudioInputDeviceCapabilities* default_device_ = nullptr;
+ const mojom::AudioInputDeviceCapabilities* mono_phone_device_ = nullptr;
+ const mojom::AudioInputDeviceCapabilities* hw_echo_canceller_device_ =
+ nullptr;
+ const mojom::AudioInputDeviceCapabilities* octagonal_device_ = nullptr;
+ const mojom::AudioInputDeviceCapabilities* geometry_device_ = nullptr;
+ const std::vector<media::Point> kMicPositions = {{8, 8, 8}, {4, 4, 4}};
+};
+
+// 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) {
+ const AudioSettingsBoolMembers kMainSettings = {
+ &AudioCaptureSettings::hotword_enabled,
+ &AudioCaptureSettings::disable_local_echo,
+ &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.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ 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_beamforming,
+ &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_beamforming,
+ &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.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ 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);
+ CheckGeometryDefaults(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);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidDeviceID) {
+ for (const auto& device : capabilities_) {
+ constraint_factory_.basic().device_id.SetExact(
+ blink::WebString::FromASCII(device->device_id));
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDevice(*device, result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(),
+ {&AudioProcessingProperties::enable_sw_echo_cancellation},
+ result);
+ bool has_hw_echo_cancellation =
+ device->parameters.effects() & media::AudioParameters::ECHO_CANCELLER;
+ EXPECT_EQ(!has_hw_echo_cancellation,
+ result.audio_processing_properties().enable_sw_echo_cancellation);
+ if (&*device == geometry_device_) {
+ EXPECT_EQ(kMicPositions,
+ result.audio_processing_properties().goog_array_geometry);
+ } else {
+ CheckGeometryDefaults(result);
+ }
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidSampleRate) {
+ constraint_factory_.basic().sample_rate.SetExact(
+ media::AudioParameters::kTelephoneSampleRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ CheckDevice(*mono_phone_device_, result);
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// SampleRate, SampleSize, ChannelCount tests check that numeric device-related
+// constraints are handled correctly.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidSampleRate) {
+ constraint_factory_.basic().sample_rate.SetExact(666);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().sample_rate.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidSampleRate) {
+ constraint_factory_.basic().sample_rate.SetMin(80000);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one with a large-enough sample rate.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidSampleRate) {
+ constraint_factory_.basic().sample_rate.SetMax(10000);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The mono device is the only one with a small-enough sample rate.
+ CheckDevice(*mono_phone_device_, result);
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidSampleRate) {
+ constraint_factory_.basic().sample_rate.SetMin(1000);
+ constraint_factory_.basic().sample_rate.SetMax(10000);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The mono device is the only one with a sample rate in the range.
+ CheckDevice(*mono_phone_device_, result);
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeSampleRate) {
+ constraint_factory_.basic().sample_rate.SetMin(9000);
+ constraint_factory_.basic().sample_rate.SetMax(10000);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().sample_rate.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealSampleRate) {
+ constraint_factory_.basic().sample_rate.SetIdeal(10000);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The mono device is the one a sample rate closest to ideal.
+ CheckDevice(*mono_phone_device_, result);
+ } else {
+ // Content capture ignores the sample_rate constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// Sample size tests.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidSampleSize) {
+ constraint_factory_.basic().sample_size.SetExact(8);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidSampleSize) {
+ constraint_factory_.basic().sample_size.SetExact(666);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().sample_size.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidSampleSize) {
+ constraint_factory_.basic().sample_size.SetMin(20);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The device with echo canceller is the only one with a sample size that
+ // is greater than the requested minimum.
+ CheckDevice(*hw_echo_canceller_device_, result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(),
+ {&AudioProcessingProperties::enable_sw_echo_cancellation},
+ result);
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ }
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidSampleSize) {
+ constraint_factory_.basic().sample_size.SetMax(10);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one with a small-enough sample size.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidSampleSize) {
+ constraint_factory_.basic().sample_size.SetMin(3);
+ constraint_factory_.basic().sample_size.SetMax(15);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one with a sample size in the range.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeSampleSize) {
+ constraint_factory_.basic().sample_size.SetMin(10);
+ constraint_factory_.basic().sample_size.SetMax(15);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().sample_size.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealSampleSize) {
+ constraint_factory_.basic().sample_size.SetIdeal(10);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the one a sample size closest to ideal.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the sample_size constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// ChannelCount tests.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidChannelCount) {
+ constraint_factory_.basic().channel_count.SetExact(8);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, ExactInvalidChannelCount) {
+ constraint_factory_.basic().channel_count.SetExact(666);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().channel_count.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MinValidChannelCount) {
+ constraint_factory_.basic().channel_count.SetMin(7);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The device with echo canceller is the only one with a channel count that
+ // is greater than the requested minimum.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, MaxValidChannelCount) {
+ constraint_factory_.basic().channel_count.SetMax(1);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one with a small-enough channel count.
+ CheckDevice(*mono_phone_device_, result);
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, RangeValidChannelCount) {
+ constraint_factory_.basic().channel_count.SetMin(3);
+ constraint_factory_.basic().channel_count.SetMax(15);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one with a channel count in the range.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidRangeChannelCount) {
+ constraint_factory_.basic().channel_count.SetMin(3);
+ constraint_factory_.basic().channel_count.SetMax(7);
+ auto result = SelectSettings();
+ if (IsDeviceCapture()) {
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_EQ(std::string(constraint_factory_.basic().channel_count.GetName()),
+ std::string(result.failed_constraint_name()));
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.HasValue());
+ EXPECT_TRUE(result.device_id().empty());
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+ }
+}
+
+TEST_P(MediaStreamConstraintsUtilAudioTest, IdealChannelCount) {
+ constraint_factory_.basic().channel_count.SetIdeal(6);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the one the number of channels closest to ideal.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores the channel_count constraint.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// Tests the echoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithSw) {
+ for (auto set_function : kBoolSetFunctions) {
+ for (auto accessor : kFactoryAccessors) {
+ // Ideal advanced is ignored by the SelectSettings algorithm.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ 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.
+ bool enable_sw_audio_processing = IsDeviceCapture() ? value : false;
+ EXPECT_EQ(value, properties.enable_sw_echo_cancellation);
+ EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+ EXPECT_EQ(enable_sw_audio_processing,
+ properties.goog_auto_gain_control);
+ CheckGoogExperimentalEchoCancellationDefault(
+ properties, enable_sw_audio_processing);
+ EXPECT_EQ(enable_sw_audio_processing,
+ properties.goog_typing_noise_detection);
+ EXPECT_EQ(enable_sw_audio_processing,
+ properties.goog_noise_suppression);
+ EXPECT_EQ(enable_sw_audio_processing,
+ properties.goog_experimental_noise_suppression);
+ EXPECT_EQ(enable_sw_audio_processing, properties.goog_beamforming);
+ EXPECT_EQ(enable_sw_audio_processing, properties.goog_highpass_filter);
+ EXPECT_EQ(enable_sw_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 hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithHw) {
+ // With content capture, there is no hardware 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.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ continue;
+ }
+ for (bool value : kBoolValues) {
+ ResetFactory();
+ constraint_factory_.basic().device_id.SetExact(
+ blink::WebString::FromASCII(hw_echo_canceller_device_->device_id));
+ ((constraint_factory_.*accessor)().echo_cancellation.*
+ set_function)(value);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ const AudioProcessingProperties& properties =
+ result.audio_processing_properties();
+ // With hardware echo cancellation, the echo_cancellation constraint
+ // enables/disables all audio processing by default, software echo
+ // cancellation is always disabled, and hardware echo cancellation is
+ // disabled if the echo_cancellation constraint is false.
+ EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+ EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+ 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_beamforming);
+ 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(*hw_echo_canceller_device_, result);
+ }
+ }
+ }
+}
+
+// Tests the googEchoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithSw) {
+ for (auto set_function : kBoolSetFunctions) {
+ for (auto accessor : kFactoryAccessors) {
+ // Ideal advanced is ignored by the SelectSettings algorithm.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ 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.
+ EXPECT_EQ(value, properties.enable_sw_echo_cancellation);
+ EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+ CheckBoolDefaults(
+ AudioSettingsBoolMembers(),
+ {
+ &AudioProcessingProperties::enable_sw_echo_cancellation,
+ &AudioProcessingProperties::disable_hw_echo_cancellation,
+ },
+ result);
+ if (IsDeviceCapture()) {
+ CheckDevice(*default_device_, result);
+ } else {
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ }
+ }
+ }
+}
+
+// Tests the googEchoCancellation constraint with a device without hardware echo
+// cancellation.
+TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithHw) {
+ // With content capture, there is no hardware 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.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ continue;
+ }
+ for (bool value : kBoolValues) {
+ ResetFactory();
+ constraint_factory_.basic().device_id.SetExact(
+ blink::WebString::FromASCII(hw_echo_canceller_device_->device_id));
+ ((constraint_factory_.*accessor)().goog_echo_cancellation.*
+ set_function)(value);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ const AudioProcessingProperties& properties =
+ result.audio_processing_properties();
+ // With hardware echo cancellation, software echo cancellation is always
+ // disabled, and hardware echo cancellation is disabled if
+ // goog_echo_cancellation is false.
+ EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+ EXPECT_EQ(!value, properties.disable_hw_echo_cancellation);
+ CheckBoolDefaults(
+ AudioSettingsBoolMembers(),
+ {
+ &AudioProcessingProperties::enable_sw_echo_cancellation,
+ &AudioProcessingProperties::disable_hw_echo_cancellation,
+ },
+ result);
+ CheckDevice(*hw_echo_canceller_device_, 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_beamforming,
+ &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_beamforming,
+ &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.
+ if (set_function == &blink::BooleanConstraint::SetIdeal &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ 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_FALSE(
+ result.audio_processing_properties().enable_sw_echo_cancellation);
+ EXPECT_TRUE(
+ result.audio_processing_properties().disable_hw_echo_cancellation);
+ 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().sample_size.SetExact(8);
+ constraint_factory_.AddAdvanced().render_to_associated_sink.SetExact(true);
+ constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one that matches the first advanced
+ // constraint set.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores device-related constraints.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults({&AudioCaptureSettings::render_to_associated_sink},
+ {&AudioProcessingProperties::goog_audio_mirroring}, result);
+ CheckGeometryDefaults(result);
+ EXPECT_TRUE(result.render_to_associated_sink());
+ EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
+}
+
+// Test that an advanced constraint set that cannot be satisfied is ignored.
+TEST_P(MediaStreamConstraintsUtilAudioTest, AdvancedSelfConflictingConstraint) {
+ auto& advanced = constraint_factory_.AddAdvanced();
+ advanced.sample_size.SetExact(8);
+ advanced.sample_rate.SetExact(8);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ // The advanced constraint is self conflicting and ignored. The default
+ // device is selected.
+ CheckDeviceDefaults(result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// Test that an advanced constraint set that contradicts a previous constraint
+// set with a device-related constraint is ignored.
+TEST_P(MediaStreamConstraintsUtilAudioTest,
+ AdvancedConflictingDeviceConstraint) {
+ constraint_factory_.AddAdvanced().sample_size.SetExact(8);
+ constraint_factory_.AddAdvanced().sample_size.SetExact(
+ media::AudioParameters::kAudioCDSampleRate);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one that matches the first advanced
+ // constraint set. The second set is ignored.
+ CheckDevice(*octagonal_device_, result);
+ EXPECT_NE(media::AudioParameters::kAudioCDSampleRate,
+ result.device_parameters().sample_rate());
+ } else {
+ // Content capture ignores device-related constraints.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ CheckGeometryDefaults(result);
+}
+
+// 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().sample_size.SetExact(8);
+ auto& advanced2 = constraint_factory_.AddAdvanced();
+ advanced2.sample_rate.SetExact(123456);
+ advanced2.hotword_enabled.SetExact(true);
+ constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one that matches the first advanced
+ // constraint set.
+ CheckDevice(*octagonal_device_, result);
+ // Second advanced set is discarded because no device has the requested
+ // sample rate.
+ EXPECT_NE(123456, result.device_parameters().sample_rate());
+ EXPECT_FALSE(result.hotword_enabled());
+ } else {
+ // Content capture ignores device-related constraints. Thus, it does not
+ // discard the second advanced set.
+ EXPECT_TRUE(result.device_id().empty());
+ EXPECT_TRUE(result.hotword_enabled());
+ }
+ CheckBoolDefaults({&AudioCaptureSettings::render_to_associated_sink,
+ &AudioCaptureSettings::hotword_enabled},
+ {&AudioProcessingProperties::goog_audio_mirroring}, result);
+ CheckGeometryDefaults(result);
+ EXPECT_TRUE(result.audio_processing_properties().goog_audio_mirroring);
+}
+
+// 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().sample_size.SetExact(8);
+ 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());
+ if (IsDeviceCapture()) {
+ // The octagonal device is the only one that matches the first advanced
+ // constraint set.
+ CheckDevice(*octagonal_device_, result);
+ } else {
+ // Content capture ignores device-related constraints.
+ EXPECT_TRUE(result.device_id().empty());
+ }
+ CheckBoolDefaults({&AudioCaptureSettings::hotword_enabled},
+ {&AudioProcessingProperties::goog_audio_mirroring}, result);
+ CheckGeometryDefaults(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);
+}
+
+// Test that a valid geometry is interpreted correctly in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, ValidGeometry) {
+ const blink::WebString kGeometry =
+ blink::WebString::FromASCII("-0.02 0 0 0.02 0 1.01");
+
+ for (auto set_function : kStringSetFunctions) {
+ for (auto accessor : kFactoryAccessors) {
+ // Ideal advanced is ignored by the SelectSettings algorithm.
+ // Using kStringSetFunctions[1] instead of
+ // static_cast<StringSetFunction>(&blink::StringConstraint::SetIdeal)
+ // because the equality comparison provides the wrong result in the
+ // Windows Debug build, making the test fail.
hbos_chromium 2017/06/14 08:12:12 lol wtf
+ if (set_function == kStringSetFunctions[1] &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ continue;
+ }
+ ResetFactory();
+ ((constraint_factory_.*accessor)().goog_array_geometry.*
+ set_function)(kGeometry);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDeviceDefaults(result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(),
+ AudioPropertiesBoolMembers(), result);
+ const std::vector<media::Point>& geometry =
+ result.audio_processing_properties().goog_array_geometry;
+ EXPECT_EQ(2u, geometry.size());
+ EXPECT_EQ(media::Point(-0.02, 0, 0), geometry[0]);
+ EXPECT_EQ(media::Point(0.02, 0, 1.01), geometry[1]);
+ }
+ }
+}
+
+// Test that an invalid geometry is interpreted as empty in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, InvalidGeometry) {
+ const blink::WebString kGeometry =
+ blink::WebString::FromASCII("1 1 1 invalid");
+
+ for (auto set_function : kStringSetFunctions) {
+ for (auto accessor : kFactoryAccessors) {
+ // Ideal advanced is ignored by the SelectSettings algorithm.
+ if (set_function == static_cast<StringSetFunction>(
+ &blink::StringConstraint::SetIdeal) &&
+ accessor == &MockConstraintFactory::AddAdvanced) {
+ continue;
+ }
+ ResetFactory();
+ ((constraint_factory_.*accessor)().goog_array_geometry.*
+ set_function)(kGeometry);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDeviceDefaults(result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(),
+ AudioPropertiesBoolMembers(), result);
+ const std::vector<media::Point>& geometry =
+ result.audio_processing_properties().goog_array_geometry;
+ EXPECT_EQ(0u, geometry.size());
+ }
+ }
+}
+
+// Test that an invalid geometry is interpreted as empty in all the ways it can
+// be set.
+TEST_P(MediaStreamConstraintsUtilAudioTest, DeviceGeometry) {
+ if (!IsDeviceCapture())
+ return;
+
+ constraint_factory_.basic().device_id.SetExact(
+ blink::WebString::FromASCII(geometry_device_->device_id));
+
+ {
+ const blink::WebString kValidGeometry =
+ blink::WebString::FromASCII("-0.02 0 0 0.02 0 1.01");
+ constraint_factory_.basic().goog_array_geometry.SetExact(kValidGeometry);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDevice(*geometry_device_, result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ // Constraints geometry should be preferred over device geometry.
+ const std::vector<media::Point>& geometry =
+ result.audio_processing_properties().goog_array_geometry;
+ EXPECT_EQ(2u, geometry.size());
+ EXPECT_EQ(media::Point(-0.02, 0, 0), geometry[0]);
+ EXPECT_EQ(media::Point(0.02, 0, 1.01), geometry[1]);
+ }
+
+ {
+ constraint_factory_.basic().goog_array_geometry.SetExact(
+ blink::WebString());
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDevice(*geometry_device_, result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ // Empty geometry is valid and should be preferred over device geometry.
+ EXPECT_TRUE(
+ result.audio_processing_properties().goog_array_geometry.empty());
+ }
+
+ {
+ const blink::WebString kInvalidGeometry =
+ blink::WebString::FromASCII("1 1 1 invalid");
+ constraint_factory_.basic().goog_array_geometry.SetExact(kInvalidGeometry);
+ auto result = SelectSettings();
+ EXPECT_TRUE(result.HasValue());
+ CheckDevice(*geometry_device_, result);
+ CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
+ result);
+ // Device geometry should be preferred over invalid constraints geometry.
+ EXPECT_EQ(kMicPositions,
+ result.audio_processing_properties().goog_array_geometry);
+ }
+}
+
+// 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());
+ 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());
+ EXPECT_FALSE(result.HasValue());
+ EXPECT_TRUE(std::string(result.failed_constraint_name()).empty());
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ MediaStreamConstraintsUtilAudioTest,
+ testing::Values("",
+ kMediaStreamSourceTab,
+ kMediaStreamSourceSystem,
+ kMediaStreamSourceDesktop));
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698