blob: 6e4c556c9decde2ce7b1e2905bf121f20b4208bf [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/modules/mediastream/media_constraints.h"
#include <math.h>
#include "base/containers/contains.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
namespace blink {
namespace {
template <typename T>
void MaybeEmitNamedNumber(StringBuilder& builder,
bool emit,
const char* name,
T value) {
if (!emit) {
return;
}
if (builder.length() > 1) {
builder.Append(", ");
}
builder.Append("\"");
builder.Append(name);
builder.Append("\": ");
builder.AppendNumber(value);
}
template <typename T>
void MaybeEmitNamedString(StringBuilder& builder,
bool emit,
const char* name,
T value) {
if (!emit) {
return;
}
if (builder.length() > 1) {
builder.Append(", ");
}
builder.Append("\"");
builder.Append(name);
builder.Append("\": ");
builder.Append(value);
}
void MaybeEmitNamedBoolean(StringBuilder& builder,
bool emit,
const char* name,
bool value) {
if (!emit) {
return;
}
if (builder.length() > 1) {
builder.Append(", ");
}
builder.Append("\"");
builder.Append(name);
builder.Append("\": ");
if (value) {
builder.Append("true");
} else {
builder.Append("false");
}
}
} // namespace
class MediaConstraintsPrivate final
: public ThreadSafeRefCounted<MediaConstraintsPrivate> {
public:
static scoped_refptr<MediaConstraintsPrivate> Create();
static scoped_refptr<MediaConstraintsPrivate> Create(
const MediaTrackConstraintSetPlatform& basic,
const Vector<MediaTrackConstraintSetPlatform>& advanced);
bool IsUnconstrained() const;
const MediaTrackConstraintSetPlatform& Basic() const;
MediaTrackConstraintSetPlatform& MutableBasic();
const Vector<MediaTrackConstraintSetPlatform>& Advanced() const;
const String ToString() const;
private:
MediaConstraintsPrivate(
const MediaTrackConstraintSetPlatform& basic,
const Vector<MediaTrackConstraintSetPlatform>& advanced);
MediaTrackConstraintSetPlatform basic_;
Vector<MediaTrackConstraintSetPlatform> advanced_;
};
scoped_refptr<MediaConstraintsPrivate> MediaConstraintsPrivate::Create() {
MediaTrackConstraintSetPlatform basic;
Vector<MediaTrackConstraintSetPlatform> advanced;
return base::AdoptRef(new MediaConstraintsPrivate(basic, advanced));
}
scoped_refptr<MediaConstraintsPrivate> MediaConstraintsPrivate::Create(
const MediaTrackConstraintSetPlatform& basic,
const Vector<MediaTrackConstraintSetPlatform>& advanced) {
return base::AdoptRef(new MediaConstraintsPrivate(basic, advanced));
}
MediaConstraintsPrivate::MediaConstraintsPrivate(
const MediaTrackConstraintSetPlatform& basic,
const Vector<MediaTrackConstraintSetPlatform>& advanced)
: basic_(basic), advanced_(advanced) {}
bool MediaConstraintsPrivate::IsUnconstrained() const {
// TODO(hta): When generating advanced constraints, make sure no empty
// elements can be added to the m_advanced vector.
return basic_.IsUnconstrained() && advanced_.empty();
}
const MediaTrackConstraintSetPlatform& MediaConstraintsPrivate::Basic() const {
return basic_;
}
MediaTrackConstraintSetPlatform& MediaConstraintsPrivate::MutableBasic() {
return basic_;
}
const Vector<MediaTrackConstraintSetPlatform>&
MediaConstraintsPrivate::Advanced() const {
return advanced_;
}
const String MediaConstraintsPrivate::ToString() const {
StringBuilder builder;
if (!IsUnconstrained()) {
builder.Append('{');
builder.Append(Basic().ToString());
if (!Advanced().empty()) {
if (builder.length() > 1) {
builder.Append(", ");
}
builder.Append("\"advanced\": [");
bool first = true;
for (const auto& constraint_set : Advanced()) {
if (!first) {
builder.Append(", ");
}
builder.Append('{');
builder.Append(constraint_set.ToString());
builder.Append('}');
first = false;
}
builder.Append(']');
}
builder.Append('}');
}
return builder.ToString();
}
// *Constraints
BaseConstraint::BaseConstraint(const char* name) : name_(name) {}
BaseConstraint::~BaseConstraint() = default;
bool BaseConstraint::HasMandatory() const {
return HasMin() || HasMax() || HasExact();
}
LongConstraint::LongConstraint(const char* name)
: BaseConstraint(name),
min_(),
max_(),
exact_(),
ideal_(),
has_min_(false),
has_max_(false),
has_exact_(false),
has_ideal_(false) {}
bool LongConstraint::Matches(int32_t value) const {
if (has_min_ && value < min_) {
return false;
}
if (has_max_ && value > max_) {
return false;
}
if (has_exact_ && value != exact_) {
return false;
}
return true;
}
bool LongConstraint::IsUnconstrained() const {
return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_;
}
void LongConstraint::ResetToUnconstrained() {
*this = LongConstraint(GetName());
}
String LongConstraint::ToString() const {
StringBuilder builder;
builder.Append('{');
MaybeEmitNamedNumber(builder, has_min_, "min", min_);
MaybeEmitNamedNumber(builder, has_max_, "max", max_);
MaybeEmitNamedNumber(builder, has_exact_, "exact", exact_);
MaybeEmitNamedNumber(builder, has_ideal_, "ideal", ideal_);
builder.Append('}');
return builder.ToString();
}
const double DoubleConstraint::kConstraintEpsilon = 0.00001;
DoubleConstraint::DoubleConstraint(const char* name)
: BaseConstraint(name),
min_(),
max_(),
exact_(),
ideal_(),
has_min_(false),
has_max_(false),
has_exact_(false),
has_ideal_(false) {}
bool DoubleConstraint::Matches(double value) const {
if (has_min_ && value < min_ - kConstraintEpsilon) {
return false;
}
if (has_max_ && value > max_ + kConstraintEpsilon) {
return false;
}
if (has_exact_ &&
fabs(static_cast<double>(value) - exact_) > kConstraintEpsilon) {
return false;
}
return true;
}
bool DoubleConstraint::IsUnconstrained() const {
return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_;
}
void DoubleConstraint::ResetToUnconstrained() {
*this = DoubleConstraint(GetName());
}
String DoubleConstraint::ToString() const {
StringBuilder builder;
builder.Append('{');
MaybeEmitNamedNumber(builder, has_min_, "min", min_);
MaybeEmitNamedNumber(builder, has_max_, "max", max_);
MaybeEmitNamedNumber(builder, has_exact_, "exact", exact_);
MaybeEmitNamedNumber(builder, has_ideal_, "ideal", ideal_);
builder.Append('}');
return builder.ToString();
}
DoubleOrBooleanConstraint::DoubleOrBooleanConstraint(const char* name)
: DoubleConstraint(name) {}
bool DoubleOrBooleanConstraint::HasMandatory() const {
return DoubleConstraint::HasMandatory() || HasExactBoolean();
}
bool DoubleOrBooleanConstraint::Matches(double value) const {
return DoubleConstraint::Matches(value) && MatchesBoolean(true);
}
bool DoubleOrBooleanConstraint::MatchesBoolean(bool value) const {
if (HasExactBoolean() && ExactBoolean() != value) {
return false;
}
if (!value && DoubleConstraint::HasMandatory()) {
return false;
}
return true;
}
bool DoubleOrBooleanConstraint::IsPresentAndNotFalse() const {
if (!IsPresent()) {
return false;
}
if (HasExactBoolean()) {
DCHECK(DoubleConstraint::IsUnconstrained());
DCHECK(!HasIdealBoolean());
return ExactBoolean();
}
if (HasIdealBoolean()) {
DCHECK(DoubleConstraint::IsUnconstrained());
DCHECK(!HasExactBoolean());
return IdealBoolean();
}
return true;
}
bool DoubleOrBooleanConstraint::IsUnconstrained() const {
return DoubleConstraint::IsUnconstrained() && !HasExactBoolean() &&
!HasIdealBoolean();
}
void DoubleOrBooleanConstraint::ResetToUnconstrained() {
*this = DoubleOrBooleanConstraint(GetName());
}
String DoubleOrBooleanConstraint::ToString() const {
if (DoubleConstraint::IsUnconstrained() &&
(HasExactBoolean() || HasIdealBoolean())) {
bool value = HasExactBoolean() ? ExactBoolean() : IdealBoolean();
return value ? "true" : "false";
}
return DoubleConstraint::ToString();
}
StringConstraint::StringConstraint(const char* name)
: BaseConstraint(name), exact_(), ideal_() {}
bool StringConstraint::Matches(String value) const {
if (exact_.empty()) {
return true;
}
for (const auto& choice : exact_) {
if (value == choice) {
return true;
}
}
return false;
}
bool StringConstraint::IsUnconstrained() const {
return exact_.empty() && ideal_.empty();
}
const Vector<String>& StringConstraint::Exact() const {
return exact_;
}
const Vector<String>& StringConstraint::Ideal() const {
return ideal_;
}
void StringConstraint::ResetToUnconstrained() {
*this = StringConstraint(GetName());
}
String StringConstraint::ToString() const {
StringBuilder builder;
builder.Append('{');
if (!ideal_.empty()) {
builder.Append("\"ideal\": [");
bool first = true;
for (const auto& iter : ideal_) {
if (!first) {
builder.Append(", ");
}
builder.Append('"');
builder.Append(iter);
builder.Append('"');
first = false;
}
builder.Append(']');
}
if (!exact_.empty()) {
if (builder.length() > 1) {
builder.Append(", ");
}
builder.Append("\"exact\": [");
bool first = true;
for (const auto& iter : exact_) {
if (!first) {
builder.Append(", ");
}
builder.Append('"');
builder.Append(iter);
builder.Append('"');
}
builder.Append(']');
}
builder.Append('}');
return builder.ToString();
}
BooleanConstraint::BooleanConstraint(const char* name)
: BaseConstraint(name),
ideal_(false),
exact_(false),
has_ideal_(false),
has_exact_(false) {}
bool BooleanConstraint::Matches(bool value) const {
if (has_exact_ && static_cast<bool>(exact_) != value) {
return false;
}
return true;
}
bool BooleanConstraint::IsUnconstrained() const {
return !has_ideal_ && !has_exact_;
}
void BooleanConstraint::ResetToUnconstrained() {
*this = BooleanConstraint(GetName());
}
String BooleanConstraint::ToString() const {
StringBuilder builder;
builder.Append('{');
MaybeEmitNamedBoolean(builder, has_exact_, "exact", Exact());
MaybeEmitNamedBoolean(builder, has_ideal_, "ideal", Ideal());
builder.Append('}');
return builder.ToString();
}
BooleanOrStringConstraint::BooleanOrStringConstraint(const char* name)
: BaseConstraint(name) {}
bool BooleanOrStringConstraint::HasExact() const {
return HasExactBoolean() || HasExactString();
}
bool BooleanOrStringConstraint::HasIdeal() const {
return HasIdealBoolean() || HasIdealString();
}
bool BooleanOrStringConstraint::IsUnconstrained() const {
return !HasExact() && !HasIdeal();
}
void BooleanOrStringConstraint::ResetToUnconstrained() {
*this = BooleanOrStringConstraint(GetName());
}
String BooleanOrStringConstraint::ToString() const {
StringBuilder builder;
builder.Append('{');
MaybeEmitNamedBoolean(builder, HasExactBoolean(), "exact", ExactBoolean());
MaybeEmitNamedString(builder, HasExactString(), "exact", ExactString());
MaybeEmitNamedBoolean(builder, HasIdealBoolean(), "ideal", IdealBoolean());
MaybeEmitNamedString(builder, HasIdealString(), "exact", IdealString());
builder.Append('}');
return builder.ToString();
}
MediaTrackConstraintSetPlatform::MediaTrackConstraintSetPlatform()
: width("width"),
height("height"),
aspect_ratio("aspectRatio"),
frame_rate("frameRate"),
facing_mode("facingMode"),
resize_mode("resizeMode"),
volume("volume"),
sample_rate("sampleRate"),
sample_size("sampleSize"),
echo_cancellation("echoCancellation"),
auto_gain_control("autoGainControl"),
noise_suppression("noiseSuppression"),
voice_isolation("voiceIsolation"),
latency("latency"),
channel_count("channelCount"),
device_id("deviceId"),
disable_local_echo("disableLocalEcho"),
suppress_local_audio_playback("suppressLocalAudioPlayback"),
restrict_own_audio("restrictOwnAudio"),
group_id("groupId"),
display_surface("displaySurface"),
exposure_compensation("exposureCompensation"),
exposure_time("exposureTime"),
color_temperature("colorTemperature"),
iso("iso"),
brightness("brightness"),
contrast("contrast"),
saturation("saturation"),
sharpness("sharpness"),
focus_distance("focusDistance"),
pan("pan"),
tilt("tilt"),
zoom("zoom"),
torch("torch"),
background_blur("backgroundBlur"),
background_segmentation_mask("backgroundSegmentationMask"),
eye_gaze_correction("eyeGazeCorrection"),
face_framing("faceFraming"),
media_stream_source("mediaStreamSource"),
render_to_associated_sink("chromeRenderToAssociatedSink"),
goog_noise_reduction("googNoiseReduction") {}
Vector<const BaseConstraint*> MediaTrackConstraintSetPlatform::AllConstraints()
const {
return {&width,
&height,
&aspect_ratio,
&frame_rate,
&facing_mode,
&resize_mode,
&volume,
&sample_rate,
&sample_size,
&echo_cancellation,
&auto_gain_control,
&noise_suppression,
&voice_isolation,
&latency,
&channel_count,
&device_id,
&group_id,
&display_surface,
&media_stream_source,
&disable_local_echo,
&suppress_local_audio_playback,
&restrict_own_audio,
&exposure_compensation,
&exposure_time,
&color_temperature,
&iso,
&brightness,
&contrast,
&saturation,
&sharpness,
&focus_distance,
&pan,
&tilt,
&zoom,
&torch,
&background_blur,
&background_segmentation_mask,
&eye_gaze_correction,
&face_framing,
&render_to_associated_sink,
&goog_noise_reduction};
}
bool MediaTrackConstraintSetPlatform::IsUnconstrained() const {
for (auto* const constraint : AllConstraints()) {
if (!constraint->IsUnconstrained()) {
return false;
}
}
return true;
}
bool MediaTrackConstraintSetPlatform::HasMandatoryOutsideSet(
const Vector<String>& good_names,
String& found_name) const {
for (auto* const constraint : AllConstraints()) {
if (constraint->HasMandatory()) {
if (!base::Contains(good_names, constraint->GetName())) {
found_name = constraint->GetName();
return true;
}
}
}
return false;
}
bool MediaTrackConstraintSetPlatform::HasMandatory() const {
String dummy_string;
return HasMandatoryOutsideSet(Vector<String>(), dummy_string);
}
bool MediaTrackConstraintSetPlatform::HasMin() const {
for (auto* const constraint : AllConstraints()) {
if (constraint->HasMin()) {
return true;
}
}
return false;
}
bool MediaTrackConstraintSetPlatform::HasExact() const {
for (auto* const constraint : AllConstraints()) {
if (constraint->HasExact()) {
return true;
}
}
return false;
}
String MediaTrackConstraintSetPlatform::ToString() const {
StringBuilder builder;
bool first = true;
for (auto* const constraint : AllConstraints()) {
if (constraint->IsPresent()) {
if (!first) {
builder.Append(", ");
}
builder.Append("\"");
builder.Append(constraint->GetName());
builder.Append("\": ");
builder.Append(constraint->ToString());
first = false;
}
}
return builder.ToString();
}
// MediaConstraints
void MediaConstraints::Assign(const MediaConstraints& other) {
private_ = other.private_;
}
MediaConstraints::MediaConstraints() = default;
MediaConstraints::MediaConstraints(const MediaConstraints& other) {
Assign(other);
}
void MediaConstraints::Reset() {
private_.Reset();
}
bool MediaConstraints::IsUnconstrained() const {
return private_.IsNull() || private_->IsUnconstrained();
}
void MediaConstraints::Initialize() {
DCHECK(IsNull());
private_ = MediaConstraintsPrivate::Create();
}
void MediaConstraints::Initialize(
const MediaTrackConstraintSetPlatform& basic,
const Vector<MediaTrackConstraintSetPlatform>& advanced) {
DCHECK(IsNull());
private_ = MediaConstraintsPrivate::Create(basic, advanced);
}
const MediaTrackConstraintSetPlatform& MediaConstraints::Basic() const {
DCHECK(!IsNull());
return private_->Basic();
}
MediaTrackConstraintSetPlatform& MediaConstraints::MutableBasic() {
DCHECK(!IsNull());
return private_->MutableBasic();
}
const Vector<MediaTrackConstraintSetPlatform>& MediaConstraints::Advanced()
const {
DCHECK(!IsNull());
return private_->Advanced();
}
const String MediaConstraints::ToString() const {
if (IsNull()) {
return g_empty_string;
}
return private_->ToString();
}
} // namespace blink