blob: 00550c484097c1bf574b1c03a6638e203c9914f3 [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 "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <utility>
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "build/build_config.h"
#include "media/base/audio_parameters.h"
#include "media/webrtc/webrtc_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/mediastream/web_platform_media_stream_source.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h"
#include "third_party/blink/renderer/modules/mediastream/mock_constraint_factory.h"
#include "third_party/blink/renderer/modules/mediastream/processed_local_audio_source.h"
#include "third_party/blink/renderer/modules/webrtc/webrtc_audio_device_impl.h"
#include "third_party/blink/renderer/platform/mediastream/media_constraints.h"
#include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
#include "third_party/webrtc/rtc_base/ref_counted_object.h"
namespace blink {
using blink::AudioCaptureSettings;
using blink::AudioProcessingProperties;
using EchoCancellationType = AudioProcessingProperties::EchoCancellationType;
using ProcessingType = AudioCaptureSettings::ProcessingType;
namespace {
using BoolSetFunction = void (blink::BooleanConstraint::*)(bool);
using StringSetFunction =
void (blink::StringConstraint::*)(const blink::WebString&);
using MockFactoryAccessor =
MediaTrackConstraintSetPlatform& (blink::MockConstraintFactory::*)();
const BoolSetFunction kBoolSetFunctions[] = {
&blink::BooleanConstraint::SetExact,
&blink::BooleanConstraint::SetIdeal,
};
const MockFactoryAccessor kFactoryAccessors[] = {
&blink::MockConstraintFactory::basic,
&blink::MockConstraintFactory::AddAdvanced};
const bool kBoolValues[] = {true, false};
const int kMinChannels = 1;
using AudioSettingsBoolMembers =
std::vector<bool (AudioCaptureSettings::*)() const>;
using AudioPropertiesBoolMembers =
std::vector<bool AudioProcessingProperties::*>;
const AudioPropertiesBoolMembers kAudioProcessingProperties = {
&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_auto_gain_control,
&AudioProcessingProperties::goog_experimental_echo_cancellation,
&AudioProcessingProperties::goog_noise_suppression,
&AudioProcessingProperties::goog_experimental_noise_suppression,
&AudioProcessingProperties::goog_highpass_filter,
&AudioProcessingProperties::goog_experimental_auto_gain_control};
template <typename T>
static bool Contains(const std::vector<T>& vector, T value) {
return base::Contains(vector, value);
}
} // namespace
class MediaStreamConstraintsUtilAudioTestBase {
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(
String::FromUTF8(GetMediaStreamSource()));
}
// If not overridden, this function will return device capture by default.
virtual std::string GetMediaStreamSource() { return std::string(); }
bool IsDeviceCapture() { return GetMediaStreamSource().empty(); }
blink::mojom::MediaStreamType GetMediaStreamType() {
std::string media_source = GetMediaStreamSource();
if (media_source.empty())
return blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
else if (media_source == blink::kMediaStreamSourceTab)
return blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE;
return blink::mojom::MediaStreamType::GUM_DESKTOP_AUDIO_CAPTURE;
}
std::unique_ptr<ProcessedLocalAudioSource> GetProcessedLocalAudioSource(
const AudioProcessingProperties& properties,
bool disable_local_echo,
bool render_to_associated_sink,
int effects) {
blink::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>(
nullptr /*web_frame*/, device, disable_local_echo, properties,
base::NullCallback(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting());
}
std::unique_ptr<ProcessedLocalAudioSource> GetProcessedLocalAudioSource(
const AudioProcessingProperties& properties,
bool disable_local_echo,
bool render_to_associated_sink) {
return GetProcessedLocalAudioSource(
properties, disable_local_echo, render_to_associated_sink,
media::AudioParameters::PlatformEffectsMask::NO_EFFECTS);
}
std::unique_ptr<blink::LocalMediaStreamAudioSource>
GetLocalMediaStreamAudioSource(
bool enable_system_echo_canceller,
bool disable_local_echo,
bool render_to_associated_sink,
bool enable_experimental_echo_canceller = false,
const int* requested_buffer_size = nullptr) {
blink::MediaStreamDevice device;
device.type = GetMediaStreamType();
int effects = 0;
if (enable_system_echo_canceller)
effects |= media::AudioParameters::ECHO_CANCELLER;
if (enable_experimental_echo_canceller)
effects |= media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER;
device.input.set_effects(effects);
if (render_to_associated_sink)
device.matched_output_device_id = std::string("some_device_id");
return std::make_unique<blink::LocalMediaStreamAudioSource>(
/*blink::WebLocalFrame=*/nullptr, device, requested_buffer_size,
disable_local_echo,
blink::WebPlatformMediaStreamSource::ConstraintsRepeatingCallback(),
blink::scheduler::GetSingleThreadTaskRunnerForTesting());
}
AudioCaptureSettings SelectSettings(
bool is_reconfigurable = false,
base::Optional<AudioDeviceCaptureCapabilities> capabilities =
base::nullopt) {
MediaConstraints constraints = constraint_factory_.CreateMediaConstraints();
if (capabilities) {
return SelectSettingsAudioCapture(*capabilities, constraints, false,
is_reconfigurable);
} else {
return SelectSettingsAudioCapture(capabilities_, constraints, false,
is_reconfigurable);
}
}
// 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::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_EQ(properties.goog_auto_gain_control,
properties.goog_experimental_auto_gain_control);
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::
goog_experimental_auto_gain_control)) {
EXPECT_TRUE(properties.goog_auto_gain_control);
}
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_auto_gain_control)) {
EXPECT_EQ(properties.goog_auto_gain_control,
properties.goog_experimental_auto_gain_control);
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_auto_gain_control)) {
EXPECT_TRUE(properties.goog_experimental_auto_gain_control);
}
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_echo_cancellation)) {
CheckGoogExperimentalEchoCancellationDefault(properties, true);
}
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);
}
}
void CheckBoolDefaultsContentCapture(
const AudioSettingsBoolMembers& exclude_main_settings,
const AudioPropertiesBoolMembers& exclude_audio_properties,
const AudioCaptureSettings& result) {
if (!Contains(exclude_main_settings,
&AudioCaptureSettings::disable_local_echo)) {
EXPECT_EQ(GetMediaStreamSource() != blink::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_EQ(properties.goog_auto_gain_control,
properties.goog_experimental_auto_gain_control);
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::
goog_experimental_auto_gain_control)) {
EXPECT_FALSE(properties.goog_auto_gain_control);
}
}
if (!Contains(
exclude_audio_properties,
&AudioProcessingProperties::goog_experimental_auto_gain_control)) {
EXPECT_EQ(properties.goog_auto_gain_control,
properties.goog_experimental_auto_gain_control);
if (!Contains(exclude_audio_properties,
&AudioProcessingProperties::goog_auto_gain_control)) {
EXPECT_FALSE(properties.goog_experimental_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_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);
}
}
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::kEchoCancellationAec3);
} else {
EXPECT_EQ(properties.echo_cancellation_type,
EchoCancellationType::kEchoCancellationDisabled);
}
}
void CheckProcessingType(const AudioCaptureSettings& result) {
ProcessingType expected_type = ProcessingType::kUnprocessed;
const auto& properties = result.audio_processing_properties();
bool properties_value = false;
// Skip audio mirroring and start directly from auto gain control.
for (size_t i = 1; i < kAudioProcessingProperties.size(); ++i)
properties_value |= properties.*kAudioProcessingProperties[i];
// If goog_audio_mirroring is true but all the other properties are false,
// we should be expecting kProcessed, however if any of the properties was
// true, we should expected kApmProcessed.
if (properties.goog_audio_mirroring && !properties_value)
expected_type = ProcessingType::kNoApmProcessed;
else if (properties_value)
expected_type = ProcessingType::kApmProcessed;
// Finally, if the chosen echo cancellation type is either AEC3 or AEC2, the
// only possible processing type to expect is kWebRtcProcessed.
if (properties.echo_cancellation_type ==
EchoCancellationType::kEchoCancellationAec3) {
expected_type = ProcessingType::kApmProcessed;
}
EXPECT_EQ(result.processing_type(), expected_type);
}
void CheckDevice(const AudioDeviceCaptureCapability& expected_device,
const AudioCaptureSettings& result) {
EXPECT_EQ(expected_device.DeviceID().Utf8(), result.device_id());
}
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) {
CheckProcessingType(result);
CheckBoolDefaults(exclude_main_settings, exclude_audio_properties, result);
CheckEchoCancellationTypeDefault(result);
CheckDeviceDefaults(result);
}
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_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_EQ(GetMediaStreamSource() != blink::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::kEchoCancellationAec3;
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;
}
void CheckLatencyConstraint(const AudioDeviceCaptureCapability* device,
double min_latency,
double max_latency) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(device->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(false);
constraint_factory_.basic().latency.SetExact(0.0);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(device->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(false);
constraint_factory_.basic().latency.SetMin(max_latency + 0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(device->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(false);
constraint_factory_.basic().latency.SetMax(min_latency - 0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
CheckLocalMediaStreamAudioSourceLatency(
device, 0.001, min_latency * device->Parameters().sample_rate());
CheckLocalMediaStreamAudioSourceLatency(
device, 1.0, max_latency * device->Parameters().sample_rate());
}
void CheckLocalMediaStreamAudioSourceLatency(
const AudioDeviceCaptureCapability* device,
double requested_latency,
int expected_buffer_size) {
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(device->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(false);
constraint_factory_.basic().latency.SetIdeal(requested_latency);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
std::unique_ptr<blink::LocalMediaStreamAudioSource> local_source =
GetLocalMediaStreamAudioSource(
false /* enable_system_echo_canceller */,
false /* disable_local_echo */,
false /* render_to_associated_sink */,
false /* enable_experimental_echo_canceller */,
base::OptionalOrNullptr(result.requested_buffer_size()));
EXPECT_EQ(local_source->GetAudioParameters().frames_per_buffer(),
expected_buffer_size);
}
blink::MockConstraintFactory constraint_factory_;
AudioDeviceCaptureCapabilities capabilities_;
const AudioDeviceCaptureCapability* default_device_ = nullptr;
const AudioDeviceCaptureCapability* system_echo_canceller_device_ = nullptr;
const AudioDeviceCaptureCapability* four_channels_device_ = nullptr;
const AudioDeviceCaptureCapability* variable_latency_device_ = nullptr;
std::unique_ptr<ProcessedLocalAudioSource> system_echo_canceller_source_ =
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:
ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
};
class MediaStreamConstraintsUtilAudioTest
: public MediaStreamConstraintsUtilAudioTestBase,
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);
capabilities_.emplace_back(
"4_channels_device", "fake_group3",
media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_4_0,
media::AudioParameters::kAudioCDSampleRate,
1000));
capabilities_.emplace_back(
"8khz_sample_rate_device", "fake_group4",
media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
blink::AudioProcessing::kSampleRate8kHz,
1000));
capabilities_.emplace_back(
"variable_latency_device", "fake_group5",
media::AudioParameters(
media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_STEREO,
media::AudioParameters::kAudioCDSampleRate, 512,
media::AudioParameters::HardwareCapabilities(128, 4096)));
default_device_ = &capabilities_[0];
system_echo_canceller_device_ = &capabilities_[1];
four_channels_device_ = &capabilities_[2];
variable_latency_device_ = &capabilities_[4];
} else {
// For content capture, use a single capability that admits all possible
// settings.
capabilities_.emplace_back();
}
}
std::string GetMediaStreamSource() override { return GetParam(); }
};
// 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) {
AudioSettingsBoolMembers kMainSettings = {
&AudioCaptureSettings::disable_local_echo,
&AudioCaptureSettings::render_to_associated_sink};
const std::vector<blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kMainBoolConstraints = {
&MediaTrackConstraintSetPlatform::disable_local_echo,
&MediaTrackConstraintSetPlatform::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 std::vector<blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kAudioProcessingConstraints = {
&MediaTrackConstraintSetPlatform::goog_audio_mirroring,
&MediaTrackConstraintSetPlatform::goog_auto_gain_control,
&MediaTrackConstraintSetPlatform::goog_experimental_echo_cancellation,
&MediaTrackConstraintSetPlatform::goog_noise_suppression,
&MediaTrackConstraintSetPlatform::goog_experimental_noise_suppression,
&MediaTrackConstraintSetPlatform::goog_highpass_filter,
&MediaTrackConstraintSetPlatform::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);
}
}
}
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, SampleSize) {
ResetFactory();
constraint_factory_.basic().sample_size.SetExact(16);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetExact(0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Only set a min value for the constraint.
ResetFactory();
constraint_factory_.basic().sample_size.SetMin(16);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetMin(17);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Only set a max value for the constraint.
ResetFactory();
constraint_factory_.basic().sample_size.SetMax(16);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetMax(15);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Define a bounded range for the constraint.
ResetFactory();
constraint_factory_.basic().sample_size.SetMin(10);
constraint_factory_.basic().sample_size.SetMax(20);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetMin(-10);
constraint_factory_.basic().sample_size.SetMax(10);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetMin(20);
constraint_factory_.basic().sample_size.SetMax(30);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test ideal constraints.
ResetFactory();
constraint_factory_.basic().sample_size.SetIdeal(16);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
ResetFactory();
constraint_factory_.basic().sample_size.SetIdeal(0);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
}
TEST_P(MediaStreamConstraintsUtilAudioTest, Channels) {
int channel_count = kMinChannels;
AudioCaptureSettings result;
// Test set exact channelCount.
for (; channel_count <= media::limits::kMaxChannels; ++channel_count) {
ResetFactory();
constraint_factory_.basic().channel_count.SetExact(channel_count);
result = SelectSettings();
if (!IsDeviceCapture()) {
// The source capture configured above is actually using a channel count
// set to 2 channels.
if (channel_count <= 2)
EXPECT_TRUE(result.HasValue());
else
EXPECT_FALSE(result.HasValue());
continue;
}
if (channel_count == 3 || channel_count > 4) {
EXPECT_FALSE(result.HasValue());
continue;
}
EXPECT_TRUE(result.HasValue());
if (channel_count == 4)
EXPECT_EQ(result.device_id(), "4_channels_device");
else
EXPECT_EQ(result.device_id(), "default_device");
}
// Only set a min value for the constraint.
ResetFactory();
constraint_factory_.basic().channel_count.SetMin(media::limits::kMaxChannels +
1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().channel_count.SetMin(kMinChannels);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Only set a max value for the constraint.
ResetFactory();
constraint_factory_.basic().channel_count.SetMax(kMinChannels - 1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().channel_count.SetMax(kMinChannels);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
// Define a bounded range for the constraint.
ResetFactory();
constraint_factory_.basic().channel_count.SetMin(kMinChannels);
constraint_factory_.basic().channel_count.SetMax(media::limits::kMaxChannels);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().channel_count.SetMin(kMinChannels - 10);
constraint_factory_.basic().channel_count.SetMax(kMinChannels - 1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().channel_count.SetMin(media::limits::kMaxChannels +
1);
constraint_factory_.basic().channel_count.SetMax(media::limits::kMaxChannels +
10);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test ideal constraints.
for (; channel_count <= media::limits::kMaxChannels; ++channel_count) {
ResetFactory();
constraint_factory_.basic().channel_count.SetExact(channel_count);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture()) {
if (channel_count == 4)
EXPECT_EQ(result.device_id(), "4_channels_device");
else
EXPECT_EQ(result.device_id(), "default_device");
}
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, ChannelsWithSource) {
if (!IsDeviceCapture())
return;
std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
false /* disable_local_echo */,
false /* render_to_associated_sink */);
int channel_count = kMinChannels;
for (; channel_count <= media::limits::kMaxChannels; ++channel_count) {
ResetFactory();
constraint_factory_.basic().channel_count.SetExact(channel_count);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
if (channel_count == 2)
EXPECT_TRUE(result.HasValue());
else
EXPECT_FALSE(result.HasValue());
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, SampleRate) {
AudioCaptureSettings result;
int exact_sample_rate = blink::AudioProcessing::kSampleRate8kHz;
int min_sample_rate = blink::AudioProcessing::kSampleRate8kHz;
// |max_sample_rate| is different based on architecture, namely due to a
// difference on Android.
int max_sample_rate =
std::max(static_cast<int>(media::AudioParameters::kAudioCDSampleRate),
blink::kAudioProcessingSampleRate);
int ideal_sample_rate = blink::AudioProcessing::kSampleRate8kHz;
if (!IsDeviceCapture()) {
exact_sample_rate = media::AudioParameters::kAudioCDSampleRate;
min_sample_rate =
std::min(static_cast<int>(media::AudioParameters::kAudioCDSampleRate),
blink::kAudioProcessingSampleRate);
ideal_sample_rate = media::AudioParameters::kAudioCDSampleRate;
}
// Test set exact sampleRate.
ResetFactory();
constraint_factory_.basic().sample_rate.SetExact(exact_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().sample_rate.SetExact(11111);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Only set a min value for the constraint.
ResetFactory();
constraint_factory_.basic().sample_rate.SetMin(max_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "default_device");
constraint_factory_.basic().sample_rate.SetMin(max_sample_rate + 1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Only set a max value for the constraint.
ResetFactory();
constraint_factory_.basic().sample_rate.SetMax(min_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().sample_rate.SetMax(min_sample_rate - 1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Define a bounded range for the constraint.
ResetFactory();
constraint_factory_.basic().sample_rate.SetMin(min_sample_rate);
constraint_factory_.basic().sample_rate.SetMax(max_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "default_device");
constraint_factory_.basic().sample_rate.SetMin(min_sample_rate - 1000);
constraint_factory_.basic().sample_rate.SetMax(min_sample_rate - 1);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().sample_rate.SetMin(max_sample_rate + 1);
constraint_factory_.basic().sample_rate.SetMax(max_sample_rate + 1000);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test ideal constraints.
ResetFactory();
constraint_factory_.basic().sample_rate.SetIdeal(ideal_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().sample_rate.SetIdeal(ideal_sample_rate);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
if (IsDeviceCapture()) {
constraint_factory_.basic().sample_rate.SetIdeal(
blink::AudioProcessing::kSampleRate48kHz + 1000);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(result.device_id(), "default_device");
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, SampleRateWithSource) {
if (!IsDeviceCapture())
return;
std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
false /* disable_local_echo */,
false /* render_to_associated_sink */);
// Test set exact sampleRate.
ResetFactory();
constraint_factory_.basic().sample_rate.SetExact(
media::AudioParameters::kAudioCDSampleRate);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().sample_rate.SetExact(11111);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Test set min sampleRate.
ResetFactory();
constraint_factory_.basic().sample_rate.SetMin(
media::AudioParameters::kAudioCDSampleRate);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().sample_rate.SetMin(
media::AudioParameters::kAudioCDSampleRate + 1);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Test set max sampleRate.
ResetFactory();
constraint_factory_.basic().sample_rate.SetMax(
media::AudioParameters::kAudioCDSampleRate);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().sample_rate.SetMax(
media::AudioParameters::kAudioCDSampleRate - 1);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Test set ideal sampleRate.
ResetFactory();
constraint_factory_.basic().sample_rate.SetIdeal(
media::AudioParameters::kAudioCDSampleRate);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().sample_rate.SetIdeal(
media::AudioParameters::kAudioCDSampleRate - 1);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
TEST_P(MediaStreamConstraintsUtilAudioTest, Latency) {
// Test set exact sampleRate.
ResetFactory();
if (IsDeviceCapture())
constraint_factory_.basic().latency.SetExact(0.125);
else
constraint_factory_.basic().latency.SetExact(0.01);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().latency.SetExact(
static_cast<double>(blink::kFallbackAudioLatencyMs) / 1000);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "default_device");
constraint_factory_.basic().latency.SetExact(0.0);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set min sampleRate.
ResetFactory();
if (IsDeviceCapture())
constraint_factory_.basic().latency.SetMin(0.125);
else
constraint_factory_.basic().latency.SetMin(0.01);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().latency.SetMin(0.126);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set max sampleRate.
ResetFactory();
constraint_factory_.basic().latency.SetMax(0.1);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "default_device");
constraint_factory_.basic().latency.SetMax(0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set bounded sampleRate range.
ResetFactory();
if (IsDeviceCapture()) {
constraint_factory_.basic().latency.SetMin(0.1);
constraint_factory_.basic().latency.SetMax(0.125);
} else {
constraint_factory_.basic().latency.SetMin(0.01);
constraint_factory_.basic().latency.SetMax(0.1);
}
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().latency.SetMin(0.0001);
constraint_factory_.basic().latency.SetMax(0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().latency.SetMin(0.126);
constraint_factory_.basic().latency.SetMax(0.2);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set ideal sampleRate range.
ResetFactory();
if (IsDeviceCapture())
constraint_factory_.basic().latency.SetIdeal(0.125);
else
constraint_factory_.basic().latency.SetIdeal(0.01);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "8khz_sample_rate_device");
constraint_factory_.basic().latency.SetIdeal(0.0);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
if (IsDeviceCapture())
EXPECT_EQ(result.device_id(), "default_device");
}
TEST_P(MediaStreamConstraintsUtilAudioTest, LatencyWithSource) {
if (!IsDeviceCapture())
return;
std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
false /* disable_local_echo */,
false /* render_to_associated_sink */);
// Test set exact sampleRate.
ResetFactory();
constraint_factory_.basic().latency.SetExact(0.01);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().latency.SetExact(0.1234);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
// Test set min sampleRate.
ResetFactory();
constraint_factory_.basic().latency.SetMin(0.01);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().latency.SetMin(0.2);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set max sampleRate.
ResetFactory();
constraint_factory_.basic().latency.SetMax(
static_cast<double>(blink::kFallbackAudioLatencyMs) / 1000);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().latency.SetMax(0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set bounded sampleRate range.
ResetFactory();
constraint_factory_.basic().latency.SetMin(0.01);
constraint_factory_.basic().latency.SetMax(0.1);
result = SelectSettings();
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().latency.SetMin(0.0001);
constraint_factory_.basic().latency.SetMax(0.001);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
constraint_factory_.basic().latency.SetMin(0.2);
constraint_factory_.basic().latency.SetMax(0.4);
result = SelectSettings();
EXPECT_FALSE(result.HasValue());
// Test set ideal sampleRate.
ResetFactory();
constraint_factory_.basic().latency.SetIdeal(0.01);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.basic().latency.SetIdeal(0.1234);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
// DeviceID tests.
TEST_P(MediaStreamConstraintsUtilAudioTest, ExactArbitraryDeviceID) {
const String kArbitraryDeviceID = "arbitrary";
constraint_factory_.basic().device_id.SetExact(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.Utf8(), 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 String kArbitraryDeviceID = "arbitrary";
constraint_factory_.basic().device_id.SetIdeal(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.Utf8(), result.device_id());
CheckProcessingType(result);
CheckBoolDefaults(AudioSettingsBoolMembers(), AudioPropertiesBoolMembers(),
result);
CheckEchoCancellationTypeDefault(result);
}
TEST_P(MediaStreamConstraintsUtilAudioTest, ExactValidDeviceID) {
for (const auto& device : capabilities_) {
constraint_factory_.basic().device_id.SetExact(device.DeviceID());
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDevice(device, result);
CheckProcessingType(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::kEchoCancellationAec3;
}
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(device.GroupID());
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckDevice(device, result);
CheckProcessingType(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::kEchoCancellationAec3;
}
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::kEchoCancellationAec3
: 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_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_EQ(GetMediaStreamSource() != blink::kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
CheckProcessingType(result);
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(
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_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_EQ(GetMediaStreamSource() != blink::kMediaStreamSourceDesktop,
result.disable_local_echo());
EXPECT_FALSE(result.render_to_associated_sink());
CheckProcessingType(result);
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::kEchoCancellationAec3
: EchoCancellationType::kEchoCancellationDisabled;
EXPECT_EQ(expected_echo_cancellation_type,
properties.echo_cancellation_type);
CheckProcessingType(result);
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(
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);
CheckProcessingType(result);
CheckBoolDefaults(AudioSettingsBoolMembers(),
AudioPropertiesBoolMembers(), result);
CheckDevice(*system_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());
}
}
// Test that having differing mandatory values for googAutoGainControl and
// googAutoGainControl2 fails. This test is valid to correctly support the
// old syntax.
TEST_P(MediaStreamConstraintsUtilAudioTest, ContradictoryAutoGainControl) {
// TODO(armax): fix this.
for (bool value : kBoolValues) {
constraint_factory_.basic().goog_auto_gain_control.SetExact(value);
constraint_factory_.basic().goog_experimental_auto_gain_control.SetExact(
!value);
auto result = SelectSettings();
EXPECT_FALSE(result.HasValue());
EXPECT_EQ(result.failed_constraint_name(),
constraint_factory_.basic()
.goog_experimental_auto_gain_control.GetName());
}
}
// Tests that individual boolean audio-processing constraints override the
// default value set by the echoCancellation constraint.
TEST_P(MediaStreamConstraintsUtilAudioTest,
EchoCancellationAndSingleBoolConstraint) {
const std::vector<blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kAudioProcessingConstraints = {
&MediaTrackConstraintSetPlatform::goog_audio_mirroring,
&MediaTrackConstraintSetPlatform::goog_auto_gain_control,
&MediaTrackConstraintSetPlatform::goog_experimental_echo_cancellation,
&MediaTrackConstraintSetPlatform::goog_noise_suppression,
&MediaTrackConstraintSetPlatform::goog_experimental_noise_suppression,
&MediaTrackConstraintSetPlatform::goog_highpass_filter,
&MediaTrackConstraintSetPlatform::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());
CheckProcessingType(result);
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;
// goog_auto_gain_control and goog_experimental_auto_gain_control
// should always match in value.
if ((i == 1 && j == 6) || (i == 6 && j == 1)) {
EXPECT_EQ(result.audio_processing_properties().*
kAudioProcessingProperties[i],
result.audio_processing_properties().*
kAudioProcessingProperties[j]);
continue;
}
EXPECT_FALSE(result.audio_processing_properties().*
kAudioProcessingProperties[j]);
}
}
}
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest,
ReconfigurationSystemEchoCancellation) {
// This test is relevant only for device capture, where HW EC can be found.
if (!IsDeviceCapture())
return;
// Create a capability that is based on a already opened source with system
// echo cancellation enabled.
AudioProcessingProperties properties;
properties.echo_cancellation_type =
EchoCancellationType::kEchoCancellationSystem;
std::unique_ptr<ProcessedLocalAudioSource> system_echo_canceller_source =
GetProcessedLocalAudioSource(
properties, false /* disable_local_echo */,
false /* render_to_associated_sink */,
media::AudioParameters::PlatformEffectsMask::ECHO_CANCELLER);
AudioDeviceCaptureCapabilities capabilities = {
AudioDeviceCaptureCapability(system_echo_canceller_source.get())};
AudioDeviceCaptureCapability* system_echo_canceller_with_source =
&capabilities[0];
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
system_echo_canceller_with_source->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(true);
auto result = SelectSettings(true, capabilities);
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().device_id.SetExact(
system_echo_canceller_with_source->DeviceID());
constraint_factory_.basic().echo_cancellation.SetExact(false);
result = SelectSettings(true, capabilities);
EXPECT_FALSE(result.HasValue());
}
// 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);
CheckProcessingType(result);
CheckBoolDefaults({&AudioCaptureSettings::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);
constraint_factory_.AddAdvanced().goog_audio_mirroring.SetExact(true);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckProcessingType(result);
CheckDeviceDefaults(result);
CheckBoolDefaults({},
{&AudioProcessingProperties::goog_audio_mirroring,
&AudioProcessingProperties::goog_highpass_filter},
result);
CheckEchoCancellationTypeDefault(result);
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().goog_audio_mirroring.SetExact(true);
auto result = SelectSettings();
EXPECT_TRUE(result.HasValue());
CheckProcessingType(result);
CheckDeviceDefaults(result);
CheckBoolDefaults({},
{&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.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_.CreateMediaConstraints(), 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_.CreateMediaConstraints(), 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<blink::LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(
enable_properties /* enable_system_echo_canceller */,
enable_properties /* disable_local_echo */,
enable_properties /* render_to_associated_sink */);
// These constraints are false in |source|.
const std::vector<
blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kConstraints = {
&MediaTrackConstraintSetPlatform::echo_cancellation,
&MediaTrackConstraintSetPlatform::disable_local_echo,
&MediaTrackConstraintSetPlatform::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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i])
.SetExact(!enable_properties);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kConstraints[i]).SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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_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 /* disable_local_echo */,
use_defaults /* render_to_associated_sink */);
const std::vector<
blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kAudioProcessingConstraints = {
&MediaTrackConstraintSetPlatform::goog_audio_mirroring,
&MediaTrackConstraintSetPlatform::goog_auto_gain_control,
&MediaTrackConstraintSetPlatform::
goog_experimental_echo_cancellation,
&MediaTrackConstraintSetPlatform::goog_noise_suppression,
&MediaTrackConstraintSetPlatform::
goog_experimental_noise_suppression,
&MediaTrackConstraintSetPlatform::goog_highpass_filter,
&MediaTrackConstraintSetPlatform::
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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetExact(!(properties.*kAudioProcessingProperties[i]));
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioProcessingConstraints[i])
.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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::kEchoCancellationAec3);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(
properties.echo_cancellation_type !=
EchoCancellationType::kEchoCancellationAec3);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
// These constraints are false in |source|.
const std::vector<
blink::BooleanConstraint MediaTrackConstraintSetPlatform::*>
kAudioBrowserConstraints = {
&MediaTrackConstraintSetPlatform::disable_local_echo,
&MediaTrackConstraintSetPlatform::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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i])
.SetExact(!use_defaults);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i]).SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
(constraint_factory_.basic().*kAudioBrowserConstraints[i])
.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(!use_defaults);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_FALSE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(true);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetIdeal(false);
result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
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 /* disable_local_echo */,
false /* render_to_associated_sink */);
const String kUnusedDeviceID = "unused_device";
const 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_.CreateMediaConstraints(),
false /* should_disable_hardware_noise_suppression */);
EXPECT_TRUE(result.HasValue());
EXPECT_EQ(result.device_id(), kUnusedDeviceID.Utf8());
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_.CreateMediaConstraints(),
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::kEchoCancellationAec3);
}
}
TEST_P(MediaStreamConstraintsUtilAudioTest, ExperimetanlEcWithSource) {
std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
GetLocalMediaStreamAudioSource(
false /* enable_system_echo_canceller */,
false /* disable_local_echo */, false /* render_to_associated_sink */,
true /* enable_experimental_echo_canceller */);
constraint_factory_.Reset();
constraint_factory_.basic().echo_cancellation.SetExact(false);
auto result = SelectSettingsAudioCapture(
source.get(), constraint_factory_.CreateMediaConstraints());
EXPECT_TRUE(result.HasValue());
}
TEST_P(MediaStreamConstraintsUtilAudioTest, LatencyConstraint) {
if (!IsDeviceCapture())
return;
// The minimum is 10ms because the AudioParameters used in
// GetLocalMediaStreamAudioSource() device.input come from the default
// constructor to blink::MediaStreamDevice, which sets them to
// AudioParameters::UnavailableDeviceParams(), which uses a 10ms buffer size.
double default_device_min =
10 / static_cast<double>(base::Time::kMillisecondsPerSecond);
double default_device_max =
1000 / static_cast<double>(media::AudioParameters::kAudioCDSampleRate);
CheckLatencyConstraint(default_device_, default_device_min,
default_device_max);
CheckLocalMediaStreamAudioSourceLatency(
default_device_, 0.003,
default_device_min *
static_cast<double>(media::AudioParameters::kAudioCDSampleRate));
CheckLocalMediaStreamAudioSourceLatency(
default_device_, 0.015,
default_device_min *
static_cast<double>(media::AudioParameters::kAudioCDSampleRate));
CheckLocalMediaStreamAudioSourceLatency(default_device_, 0.022, 1000);
CheckLocalMediaStreamAudioSourceLatency(default_device_, 0.04, 1000);
double variable_latency_device_min =
128 / static_cast<double>(media::AudioParameters::kAudioCDSampleRate);
double variable_latency_device_max =
4096 / static_cast<double>(media::AudioParameters::kAudioCDSampleRate);
CheckLatencyConstraint(variable_latency_device_, variable_latency_device_min,
variable_latency_device_max);
// Values here are the closest match to the requested latency as returned by
// media::AudioLatency::GetExactBufferSize().
CheckLocalMediaStreamAudioSourceLatency(variable_latency_device_, 0.001, 128);
CheckLocalMediaStreamAudioSourceLatency(variable_latency_device_, 0.011, 512);
#if defined(OS_WIN)
// Windows only uses exactly the minimum or else multiples of the
// hardware_buffer_size (512 for the variable_latency_device_).
CheckLocalMediaStreamAudioSourceLatency(variable_latency_device_, 0.020,
1024);
#else
CheckLocalMediaStreamAudioSourceLatency(variable_latency_device_, 0.020, 896);
#endif
CheckLocalMediaStreamAudioSourceLatency(variable_latency_device_, 0.2, 4096);
}
INSTANTIATE_TEST_SUITE_P(All,
MediaStreamConstraintsUtilAudioTest,
testing::Values("",
blink::kMediaStreamSourceTab,
blink::kMediaStreamSourceSystem,
blink::kMediaStreamSourceDesktop));
} // namespace blink