blob: 98065a1997c513625f2f8df82436254988a6d996 [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:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. 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_impl.h"
#include "base/notreached.h"
#include "build/build_config.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_union_boolean_string.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_union_string_stringsequence.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_boolean_or_dom_string_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_boolean_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_dom_string_parameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_double_range.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_long_range.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_track_constraints.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_typedefs.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_constrainbooleanordomstringparameters_string.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_constrainbooleanparameters.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_boolean_constraindoublerange_double.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_constraindomstringparameters_string_stringsequence.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_constraindoublerange_double.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_constrainlongrange_long.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/modules/mediastream/media_constraints.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace media_constraints_impl {
namespace {
// A naked value is treated as an "ideal" value in the basic constraints,
// but as an exact value in "advanced" constraints.
// https://w3c.github.io/mediacapture-main/#constrainable-interface
enum class NakedValueDisposition { kTreatAsIdeal, kTreatAsExact };
// Old type/value form of constraint. Used in parsing old-style constraints.
struct NameValueStringConstraint {
NameValueStringConstraint() = default;
NameValueStringConstraint(String name, String value)
: name_(name), value_(value) {}
String name_;
String value_;
};
// Legal constraint names.
// Legacy getUserMedia() constraints. Sadly still in use.
const char kMinAspectRatio[] = "minAspectRatio";
const char kMaxAspectRatio[] = "maxAspectRatio";
const char kMaxWidth[] = "maxWidth";
const char kMinWidth[] = "minWidth";
const char kMaxHeight[] = "maxHeight";
const char kMinHeight[] = "minHeight";
const char kMaxFrameRate[] = "maxFrameRate";
const char kMinFrameRate[] = "minFrameRate";
const char kMediaStreamSource[] = "chromeMediaSource";
const char kMediaStreamSourceId[] =
"chromeMediaSourceId"; // mapped to deviceId
const char kMediaStreamSourceInfoId[] = "sourceId"; // mapped to deviceId
const char kMediaStreamRenderToAssociatedSink[] =
"chromeRenderToAssociatedSink";
// RenderToAssociatedSink will be going away some time.
const char kEchoCancellation[] = "echoCancellation";
const char kDisableLocalEcho[] = "disableLocalEcho";
// Google-specific constraint keys for a local video source (getUserMedia).
const char kNoiseReduction[] = "googNoiseReduction";
static bool ParseMandatoryConstraintsDictionary(
const Dictionary& mandatory_constraints_dictionary,
Vector<NameValueStringConstraint>& mandatory) {
DummyExceptionStateForTesting exception_state;
const HashMap<String, String>& mandatory_constraints_hash_map =
mandatory_constraints_dictionary.GetOwnPropertiesAsStringHashMap(
exception_state);
if (exception_state.HadException())
return false;
for (const auto& iter : mandatory_constraints_hash_map)
mandatory.push_back(NameValueStringConstraint(iter.key, iter.value));
return true;
}
static bool ParseOptionalConstraintsVectorElement(
const Dictionary& constraint,
Vector<NameValueStringConstraint>& optional_constraints_vector) {
DummyExceptionStateForTesting exception_state;
const Vector<String>& local_names =
constraint.GetPropertyNames(exception_state);
if (exception_state.HadException() || local_names.size() != 1) {
return false;
}
const String& key = local_names[0];
std::optional<String> value = constraint.Get<IDLString>(key, exception_state);
if (exception_state.HadException() || !value) {
return false;
}
optional_constraints_vector.push_back(NameValueStringConstraint(key, *value));
return true;
}
static bool Parse(const MediaTrackConstraints* constraints_in,
Vector<NameValueStringConstraint>& optional,
Vector<NameValueStringConstraint>& mandatory) {
Vector<NameValueStringConstraint> mandatory_constraints_vector;
if (constraints_in->hasMandatory()) {
bool ok = ParseMandatoryConstraintsDictionary(
Dictionary(constraints_in->mandatory()), mandatory);
if (!ok)
return false;
}
if (constraints_in->hasOptional()) {
for (const auto& constraint : constraints_in->optional()) {
bool ok = ParseOptionalConstraintsVectorElement(Dictionary(constraint),
optional);
if (!ok)
return false;
}
}
return true;
}
static bool ToBoolean(const String& as_string) {
return as_string == "true";
// TODO(hta): Check against "false" and return error if it's neither.
// https://crbug.com/576582
}
static void ParseOldStyleNames(
ExecutionContext* context,
const Vector<NameValueStringConstraint>& old_names,
MediaTrackConstraintSetPlatform& result) {
if (old_names.size() > 0) {
UseCounter::Count(context, WebFeature::kOldConstraintsParsed);
}
for (const NameValueStringConstraint& constraint : old_names) {
if (constraint.name_ == kMinAspectRatio) {
result.aspect_ratio.SetMin(atof(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMaxAspectRatio) {
result.aspect_ratio.SetMax(atof(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMaxWidth) {
result.width.SetMax(atoi(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMinWidth) {
result.width.SetMin(atoi(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMaxHeight) {
result.height.SetMax(atoi(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMinHeight) {
result.height.SetMin(atoi(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMinFrameRate) {
result.frame_rate.SetMin(atof(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kMaxFrameRate) {
result.frame_rate.SetMax(atof(constraint.value_.Utf8().c_str()));
} else if (constraint.name_ == kEchoCancellation) {
result.echo_cancellation.SetExactBoolean(ToBoolean(constraint.value_));
} else if (constraint.name_ == kMediaStreamSource) {
// TODO(hta): This has only a few legal values. Should be
// represented as an enum, and cause type errors.
// https://crbug.com/576582
result.media_stream_source.SetExact(constraint.value_);
} else if (constraint.name_ == kDisableLocalEcho &&
RuntimeEnabledFeatures::
DesktopCaptureDisableLocalEchoControlEnabled()) {
result.disable_local_echo.SetExact(ToBoolean(constraint.value_));
} else if (constraint.name_ == kMediaStreamSourceId ||
constraint.name_ == kMediaStreamSourceInfoId) {
result.device_id.SetExact(constraint.value_);
} else if (constraint.name_ == kMediaStreamRenderToAssociatedSink) {
// TODO(hta): This is a boolean represented as string.
// Should give TypeError when it's not parseable.
// https://crbug.com/576582
result.render_to_associated_sink.SetExact(ToBoolean(constraint.value_));
} else if (constraint.name_ == kNoiseReduction) {
result.goog_noise_reduction.SetExact(ToBoolean(constraint.value_));
}
// else: Nothing. Unrecognized constraints are simply ignored.
}
}
static MediaConstraints CreateFromNamedConstraints(
ExecutionContext* context,
Vector<NameValueStringConstraint>& mandatory,
const Vector<NameValueStringConstraint>& optional) {
MediaTrackConstraintSetPlatform basic;
MediaTrackConstraintSetPlatform advanced;
MediaConstraints constraints;
ParseOldStyleNames(context, mandatory, basic);
// We ignore unknown names and syntax errors in optional constraints.
Vector<MediaTrackConstraintSetPlatform> advanced_vector;
for (const auto& optional_constraint : optional) {
MediaTrackConstraintSetPlatform advanced_element;
Vector<NameValueStringConstraint> element_as_list(1, optional_constraint);
ParseOldStyleNames(context, element_as_list, advanced_element);
if (!advanced_element.IsUnconstrained())
advanced_vector.push_back(advanced_element);
}
constraints.Initialize(basic, advanced_vector);
return constraints;
}
void CopyLongConstraint(const V8ConstrainLong* blink_union_form,
NakedValueDisposition naked_treatment,
LongConstraint& web_form) {
web_form.SetIsPresent(true);
switch (blink_union_form->GetContentType()) {
case V8ConstrainLong::ContentType::kConstrainLongRange: {
const auto* blink_form = blink_union_form->GetAsConstrainLongRange();
if (blink_form->hasMin()) {
web_form.SetMin(blink_form->min());
}
if (blink_form->hasMax()) {
web_form.SetMax(blink_form->max());
}
if (blink_form->hasIdeal()) {
web_form.SetIdeal(blink_form->ideal());
}
if (blink_form->hasExact()) {
web_form.SetExact(blink_form->exact());
}
break;
}
case V8ConstrainLong::ContentType::kLong:
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdeal(blink_union_form->GetAsLong());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExact(blink_union_form->GetAsLong());
break;
}
break;
}
}
void CopyDoubleConstraint(const V8ConstrainDouble* blink_union_form,
NakedValueDisposition naked_treatment,
DoubleConstraint& web_form) {
web_form.SetIsPresent(true);
switch (blink_union_form->GetContentType()) {
case V8ConstrainDouble::ContentType::kConstrainDoubleRange: {
const auto* blink_form = blink_union_form->GetAsConstrainDoubleRange();
if (blink_form->hasMin()) {
web_form.SetMin(blink_form->min());
}
if (blink_form->hasMax()) {
web_form.SetMax(blink_form->max());
}
if (blink_form->hasIdeal()) {
web_form.SetIdeal(blink_form->ideal());
}
if (blink_form->hasExact()) {
web_form.SetExact(blink_form->exact());
}
break;
}
case V8ConstrainDouble::ContentType::kDouble:
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdeal(blink_union_form->GetAsDouble());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExact(blink_union_form->GetAsDouble());
break;
}
break;
}
}
void CopyBooleanOrDoubleConstraint(
const V8UnionBooleanOrConstrainDouble* blink_union_form,
NakedValueDisposition naked_treatment,
DoubleOrBooleanConstraint& web_form) {
switch (blink_union_form->GetContentType()) {
case V8UnionBooleanOrConstrainDouble::ContentType::kBoolean:
web_form.SetIsPresent(true);
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdealBoolean(blink_union_form->GetAsBoolean());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExactBoolean(blink_union_form->GetAsBoolean());
break;
}
break;
case V8UnionBooleanOrConstrainDouble::ContentType::kConstrainDoubleRange:
case V8UnionBooleanOrConstrainDouble::ContentType::kDouble:
CopyDoubleConstraint(blink_union_form->GetAsV8ConstrainDouble(),
naked_treatment, web_form);
break;
}
}
void CopyBooleanOrStringConstraint(
const V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString*
blink_union_form,
NakedValueDisposition naked_treatment,
BooleanOrStringConstraint& web_form) {
switch (blink_union_form->GetContentType()) {
case V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString::
ContentType::kBoolean:
web_form.SetIsPresent(true);
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdealBoolean(blink_union_form->GetAsBoolean());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExactBoolean(blink_union_form->GetAsBoolean());
break;
}
break;
case V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString::
ContentType::kString:
web_form.SetIsPresent(true);
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdealString(blink_union_form->GetAsString());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExactString(blink_union_form->GetAsString());
break;
}
break;
case V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString::
ContentType::kConstrainBooleanOrDOMStringParameters:
web_form.SetIsPresent(true);
ConstrainBooleanOrDOMStringParameters* boolean_or_dom_string =
blink_union_form->GetAsConstrainBooleanOrDOMStringParameters();
if (boolean_or_dom_string->hasIdeal()) {
V8UnionBooleanOrString* ideal_value = boolean_or_dom_string->ideal();
if (ideal_value->IsBoolean()) {
web_form.SetIdealBoolean(ideal_value->GetAsBoolean());
} else {
CHECK(ideal_value->IsString());
web_form.SetIdealString(ideal_value->GetAsString());
}
}
if (boolean_or_dom_string->hasExact()) {
V8UnionBooleanOrString* exact_value = boolean_or_dom_string->exact();
if (exact_value->IsBoolean()) {
web_form.SetExactBoolean(exact_value->GetAsBoolean());
} else {
CHECK(exact_value->IsString());
web_form.SetExactString(exact_value->GetAsString());
}
}
break;
}
}
bool ValidateString(const String& str, String& error_message) {
if (str.length() > kMaxConstraintStringLength) {
error_message = "Constraint string too long.";
return false;
}
return true;
}
bool ValidateStringSeq(const Vector<String>& strs, String& error_message) {
if (strs.size() > kMaxConstraintStringSeqLength) {
error_message = "Constraint string sequence too long.";
return false;
}
for (const String& str : strs) {
if (!ValidateString(str, error_message)) {
return false;
}
}
return true;
}
bool ValidateStringConstraint(
V8UnionStringOrStringSequence* string_or_string_seq,
String& error_message) {
switch (string_or_string_seq->GetContentType()) {
case V8UnionStringOrStringSequence::ContentType::kString: {
return ValidateString(string_or_string_seq->GetAsString(), error_message);
}
case V8UnionStringOrStringSequence::ContentType::kStringSequence: {
return ValidateStringSeq(string_or_string_seq->GetAsStringSequence(),
error_message);
}
}
NOTREACHED();
}
bool ValidateStringConstraint(const V8ConstrainDOMString* blink_union_form,
String& error_message) {
switch (blink_union_form->GetContentType()) {
case V8ConstrainDOMString::ContentType::kConstrainDOMStringParameters: {
const auto* blink_form =
blink_union_form->GetAsConstrainDOMStringParameters();
if (blink_form->hasIdeal() &&
!ValidateStringConstraint(blink_form->ideal(), error_message)) {
return false;
}
if (blink_form->hasExact() &&
!ValidateStringConstraint(blink_form->exact(), error_message)) {
return false;
}
return true;
}
case V8ConstrainDOMString::ContentType::kString:
return ValidateString(blink_union_form->GetAsString(), error_message);
case V8ConstrainDOMString::ContentType::kStringSequence:
return ValidateStringSeq(blink_union_form->GetAsStringSequence(),
error_message);
}
NOTREACHED();
}
[[nodiscard]] bool ValidateAndCopyStringConstraint(
const V8ConstrainDOMString* blink_union_form,
NakedValueDisposition naked_treatment,
StringConstraint& web_form,
String& error_message) {
if (!ValidateStringConstraint(blink_union_form, error_message)) {
return false;
}
web_form.SetIsPresent(true);
switch (blink_union_form->GetContentType()) {
case V8ConstrainDOMString::ContentType::kConstrainDOMStringParameters: {
const auto* blink_form =
blink_union_form->GetAsConstrainDOMStringParameters();
if (blink_form->hasIdeal()) {
switch (blink_form->ideal()->GetContentType()) {
case V8UnionStringOrStringSequence::ContentType::kString:
web_form.SetIdeal(
Vector<String>(1, blink_form->ideal()->GetAsString()));
break;
case V8UnionStringOrStringSequence::ContentType::kStringSequence:
web_form.SetIdeal(blink_form->ideal()->GetAsStringSequence());
break;
}
}
if (blink_form->hasExact()) {
switch (blink_form->exact()->GetContentType()) {
case V8UnionStringOrStringSequence::ContentType::kString:
web_form.SetExact(
Vector<String>(1, blink_form->exact()->GetAsString()));
break;
case V8UnionStringOrStringSequence::ContentType::kStringSequence:
web_form.SetExact(blink_form->exact()->GetAsStringSequence());
break;
}
}
break;
}
case V8ConstrainDOMString::ContentType::kString:
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdeal(Vector<String>(1, blink_union_form->GetAsString()));
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExact(Vector<String>(1, blink_union_form->GetAsString()));
break;
}
break;
case V8ConstrainDOMString::ContentType::kStringSequence:
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdeal(blink_union_form->GetAsStringSequence());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExact(blink_union_form->GetAsStringSequence());
break;
}
break;
}
return true;
}
void CopyBooleanConstraint(const V8ConstrainBoolean* blink_union_form,
NakedValueDisposition naked_treatment,
BooleanConstraint& web_form) {
web_form.SetIsPresent(true);
switch (blink_union_form->GetContentType()) {
case V8ConstrainBoolean::ContentType::kBoolean:
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
web_form.SetIdeal(blink_union_form->GetAsBoolean());
break;
case NakedValueDisposition::kTreatAsExact:
web_form.SetExact(blink_union_form->GetAsBoolean());
break;
}
break;
case V8ConstrainBoolean::ContentType::kConstrainBooleanParameters: {
const auto* blink_form =
blink_union_form->GetAsConstrainBooleanParameters();
if (blink_form->hasIdeal()) {
web_form.SetIdeal(blink_form->ideal());
}
if (blink_form->hasExact()) {
web_form.SetExact(blink_form->exact());
}
break;
}
}
}
bool ValidateAndCopyConstraintSet(
const MediaTrackConstraintSet* constraints_in,
NakedValueDisposition naked_treatment,
MediaTrackConstraintSetPlatform& constraint_buffer,
String& error_message) {
if (constraints_in->hasWidth()) {
CopyLongConstraint(constraints_in->width(), naked_treatment,
constraint_buffer.width);
}
if (constraints_in->hasHeight()) {
CopyLongConstraint(constraints_in->height(), naked_treatment,
constraint_buffer.height);
}
if (constraints_in->hasAspectRatio()) {
CopyDoubleConstraint(constraints_in->aspectRatio(), naked_treatment,
constraint_buffer.aspect_ratio);
}
if (constraints_in->hasFrameRate()) {
CopyDoubleConstraint(constraints_in->frameRate(), naked_treatment,
constraint_buffer.frame_rate);
}
if (constraints_in->hasFacingMode()) {
if (!ValidateAndCopyStringConstraint(
constraints_in->facingMode(), naked_treatment,
constraint_buffer.facing_mode, error_message)) {
return false;
}
}
if (constraints_in->hasResizeMode()) {
if (!ValidateAndCopyStringConstraint(
constraints_in->resizeMode(), naked_treatment,
constraint_buffer.resize_mode, error_message)) {
return false;
}
}
if (constraints_in->hasSampleRate()) {
CopyLongConstraint(constraints_in->sampleRate(), naked_treatment,
constraint_buffer.sample_rate);
}
if (constraints_in->hasSampleSize()) {
CopyLongConstraint(constraints_in->sampleSize(), naked_treatment,
constraint_buffer.sample_size);
}
if (constraints_in->hasEchoCancellation()) {
CopyBooleanOrStringConstraint(constraints_in->echoCancellation(),
naked_treatment,
constraint_buffer.echo_cancellation);
}
if (constraints_in->hasAutoGainControl()) {
CopyBooleanConstraint(constraints_in->autoGainControl(), naked_treatment,
constraint_buffer.auto_gain_control);
}
if (constraints_in->hasNoiseSuppression()) {
CopyBooleanConstraint(constraints_in->noiseSuppression(), naked_treatment,
constraint_buffer.noise_suppression);
}
if (constraints_in->hasVoiceIsolation()) {
CopyBooleanConstraint(constraints_in->voiceIsolation(), naked_treatment,
constraint_buffer.voice_isolation);
}
if (constraints_in->hasLatency()) {
CopyDoubleConstraint(constraints_in->latency(), naked_treatment,
constraint_buffer.latency);
}
if (constraints_in->hasChannelCount()) {
CopyLongConstraint(constraints_in->channelCount(), naked_treatment,
constraint_buffer.channel_count);
}
if (constraints_in->hasDeviceId()) {
if (!ValidateAndCopyStringConstraint(
constraints_in->deviceId(), naked_treatment,
constraint_buffer.device_id, error_message)) {
return false;
}
}
if (constraints_in->hasGroupId()) {
if (!ValidateAndCopyStringConstraint(
constraints_in->groupId(), naked_treatment,
constraint_buffer.group_id, error_message)) {
return false;
}
}
if (constraints_in->hasExposureCompensation()) {
CopyDoubleConstraint(constraints_in->exposureCompensation(),
naked_treatment,
constraint_buffer.exposure_compensation);
}
if (constraints_in->hasExposureTime()) {
CopyDoubleConstraint(constraints_in->exposureTime(), naked_treatment,
constraint_buffer.exposure_time);
}
if (constraints_in->hasColorTemperature()) {
CopyDoubleConstraint(constraints_in->colorTemperature(), naked_treatment,
constraint_buffer.color_temperature);
}
if (constraints_in->hasIso()) {
CopyDoubleConstraint(constraints_in->iso(), naked_treatment,
constraint_buffer.iso);
}
if (constraints_in->hasBrightness()) {
CopyDoubleConstraint(constraints_in->brightness(), naked_treatment,
constraint_buffer.brightness);
}
if (constraints_in->hasContrast()) {
CopyDoubleConstraint(constraints_in->contrast(), naked_treatment,
constraint_buffer.contrast);
}
if (constraints_in->hasSaturation()) {
CopyDoubleConstraint(constraints_in->saturation(), naked_treatment,
constraint_buffer.saturation);
}
if (constraints_in->hasSharpness()) {
CopyDoubleConstraint(constraints_in->sharpness(), naked_treatment,
constraint_buffer.sharpness);
}
if (constraints_in->hasFocusDistance()) {
CopyDoubleConstraint(constraints_in->focusDistance(), naked_treatment,
constraint_buffer.focus_distance);
}
if (constraints_in->hasPan()) {
CopyBooleanOrDoubleConstraint(constraints_in->pan(), naked_treatment,
constraint_buffer.pan);
}
if (constraints_in->hasTilt()) {
CopyBooleanOrDoubleConstraint(constraints_in->tilt(), naked_treatment,
constraint_buffer.tilt);
}
if (constraints_in->hasZoom()) {
CopyBooleanOrDoubleConstraint(constraints_in->zoom(), naked_treatment,
constraint_buffer.zoom);
}
if (constraints_in->hasTorch()) {
CopyBooleanConstraint(constraints_in->torch(), naked_treatment,
constraint_buffer.torch);
}
if (constraints_in->hasBackgroundBlur()) {
CopyBooleanConstraint(constraints_in->backgroundBlur(), naked_treatment,
constraint_buffer.background_blur);
}
if (constraints_in->hasBackgroundSegmentationMask()) {
CopyBooleanConstraint(constraints_in->backgroundSegmentationMask(),
naked_treatment,
constraint_buffer.background_segmentation_mask);
}
if (constraints_in->hasEyeGazeCorrection()) {
CopyBooleanConstraint(constraints_in->eyeGazeCorrection(), naked_treatment,
constraint_buffer.eye_gaze_correction);
}
if (constraints_in->hasFaceFraming()) {
CopyBooleanConstraint(constraints_in->faceFraming(), naked_treatment,
constraint_buffer.face_framing);
}
if (constraints_in->hasDisplaySurface()) {
if (!ValidateAndCopyStringConstraint(
constraints_in->displaySurface(), naked_treatment,
constraint_buffer.display_surface, error_message)) {
return false;
}
}
if (constraints_in->hasSuppressLocalAudioPlayback()) {
CopyBooleanConstraint(constraints_in->suppressLocalAudioPlayback(),
naked_treatment,
constraint_buffer.suppress_local_audio_playback);
}
if (constraints_in->hasRestrictOwnAudio()) {
CopyBooleanConstraint(constraints_in->restrictOwnAudio(), naked_treatment,
constraint_buffer.restrict_own_audio);
}
return true;
}
template <class T>
bool UseNakedBooleanNotNumeric(const T& input, NakedValueDisposition which) {
if (input.HasExact() || input.HasIdeal() || input.HasMin() ||
input.HasMax()) {
return false;
}
switch (which) {
case NakedValueDisposition::kTreatAsIdeal:
return input.HasIdealBoolean();
case NakedValueDisposition::kTreatAsExact:
return input.HasExactBoolean();
}
NOTREACHED();
}
template <class T>
bool UseNakedNumeric(const T& input, NakedValueDisposition which) {
switch (which) {
case NakedValueDisposition::kTreatAsIdeal:
return input.HasIdeal() &&
!(input.HasExact() || input.HasMin() || input.HasMax());
break;
case NakedValueDisposition::kTreatAsExact:
return input.HasExact() &&
!(input.HasIdeal() || input.HasMin() || input.HasMax());
break;
}
NOTREACHED();
}
template <class T>
bool UseNakedNonNumeric(const T& input, NakedValueDisposition which) {
switch (which) {
case NakedValueDisposition::kTreatAsIdeal:
return input.HasIdeal() && !input.HasExact();
break;
case NakedValueDisposition::kTreatAsExact:
return input.HasExact() && !input.HasIdeal();
break;
}
NOTREACHED();
}
template <class T>
bool GetNakedBoolean(const T& input, NakedValueDisposition which) {
switch (which) {
case NakedValueDisposition::kTreatAsIdeal:
return input.IdealBoolean();
case NakedValueDisposition::kTreatAsExact:
return input.ExactBoolean();
}
NOTREACHED();
}
template <typename U, class T>
U GetNakedValue(const T& input, NakedValueDisposition which) {
switch (which) {
case NakedValueDisposition::kTreatAsIdeal:
return input.Ideal();
break;
case NakedValueDisposition::kTreatAsExact:
return input.Exact();
break;
}
NOTREACHED();
}
V8ConstrainLong* ConvertLong(const LongConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNumeric(input, naked_treatment)) {
return MakeGarbageCollected<V8ConstrainLong>(
GetNakedValue<uint32_t>(input, naked_treatment));
} else if (!input.IsUnconstrained()) {
ConstrainLongRange* output = ConstrainLongRange::Create();
if (input.HasExact())
output->setExact(input.Exact());
if (input.HasMin())
output->setMin(input.Min());
if (input.HasMax())
output->setMax(input.Max());
if (input.HasIdeal())
output->setIdeal(input.Ideal());
return MakeGarbageCollected<V8ConstrainLong>(output);
}
return nullptr;
}
V8ConstrainDouble* ConvertDouble(const DoubleConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNumeric(input, naked_treatment)) {
return MakeGarbageCollected<V8ConstrainDouble>(
GetNakedValue<double>(input, naked_treatment));
} else if (!input.IsUnconstrained()) {
ConstrainDoubleRange* output = ConstrainDoubleRange::Create();
if (input.HasExact())
output->setExact(input.Exact());
if (input.HasIdeal())
output->setIdeal(input.Ideal());
if (input.HasMin())
output->setMin(input.Min());
if (input.HasMax())
output->setMax(input.Max());
return MakeGarbageCollected<V8ConstrainDouble>(output);
}
return nullptr;
}
V8UnionBooleanOrConstrainDouble* ConvertBooleanOrDouble(
const DoubleOrBooleanConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNumeric(input, naked_treatment)) {
return MakeGarbageCollected<V8UnionBooleanOrConstrainDouble>(
GetNakedValue<double>(input, naked_treatment));
} else if (UseNakedBooleanNotNumeric(input, naked_treatment)) {
return MakeGarbageCollected<V8UnionBooleanOrConstrainDouble>(
GetNakedBoolean(input, naked_treatment));
} else if (!input.IsUnconstrained()) {
ConstrainDoubleRange* output = ConstrainDoubleRange::Create();
if (input.HasExact())
output->setExact(input.Exact());
if (input.HasIdeal())
output->setIdeal(input.Ideal());
if (input.HasMin())
output->setMin(input.Min());
if (input.HasMax())
output->setMax(input.Max());
return MakeGarbageCollected<V8UnionBooleanOrConstrainDouble>(output);
}
return nullptr;
}
V8UnionStringOrStringSequence* ConvertStringSequence(
const Vector<String>& input) {
if (input.size() > 1) {
return MakeGarbageCollected<V8UnionStringOrStringSequence>(input);
} else if (!input.empty()) {
return MakeGarbageCollected<V8UnionStringOrStringSequence>(input[0]);
}
return nullptr;
}
V8ConstrainDOMString* ConvertString(const StringConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNonNumeric(input, naked_treatment)) {
const Vector<String>& input_buffer(
GetNakedValue<const Vector<String>&>(input, naked_treatment));
if (input_buffer.size() > 1) {
return MakeGarbageCollected<V8ConstrainDOMString>(input_buffer);
} else if (!input_buffer.empty()) {
return MakeGarbageCollected<V8ConstrainDOMString>(input_buffer[0]);
}
return nullptr;
} else if (!input.IsUnconstrained()) {
ConstrainDOMStringParameters* output =
ConstrainDOMStringParameters::Create();
if (input.HasExact())
output->setExact(ConvertStringSequence(input.Exact()));
if (input.HasIdeal())
output->setIdeal(ConvertStringSequence(input.Ideal()));
return MakeGarbageCollected<V8ConstrainDOMString>(output);
}
return nullptr;
}
V8ConstrainBoolean* ConvertBoolean(const BooleanConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNonNumeric(input, naked_treatment)) {
return MakeGarbageCollected<V8ConstrainBoolean>(
GetNakedValue<bool>(input, naked_treatment));
} else if (!input.IsUnconstrained()) {
ConstrainBooleanParameters* output = ConstrainBooleanParameters::Create();
if (input.HasExact())
output->setExact(input.Exact());
if (input.HasIdeal())
output->setIdeal(input.Ideal());
return MakeGarbageCollected<V8ConstrainBoolean>(output);
}
return nullptr;
}
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString*
ConvertBooleanOrString(const BooleanOrStringConstraint& input,
NakedValueDisposition naked_treatment) {
if (UseNakedNonNumeric(input, naked_treatment)) {
switch (naked_treatment) {
case NakedValueDisposition::kTreatAsIdeal:
if (input.HasIdealBoolean()) {
return MakeGarbageCollected<
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString>(
input.IdealBoolean());
}
if (input.HasIdealString()) {
return MakeGarbageCollected<
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString>(
input.IdealString());
}
NOTREACHED();
case NakedValueDisposition::kTreatAsExact:
if (input.HasExactBoolean()) {
return MakeGarbageCollected<
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString>(
input.ExactBoolean());
}
if (input.HasIdealString()) {
return MakeGarbageCollected<
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString>(
input.ExactString());
}
NOTREACHED();
}
} else if (!input.IsUnconstrained()) {
ConstrainBooleanOrDOMStringParameters* output =
ConstrainBooleanOrDOMStringParameters::Create();
if (input.HasExactBoolean()) {
output->setExact(
MakeGarbageCollected<V8UnionBooleanOrString>(input.ExactBoolean()));
} else if (input.HasExactString()) {
output->setExact(
MakeGarbageCollected<V8UnionBooleanOrString>(input.ExactString()));
}
if (input.HasIdealBoolean()) {
output->setIdeal(
MakeGarbageCollected<V8UnionBooleanOrString>(input.IdealBoolean()));
} else if (input.HasIdealString()) {
output->setIdeal(
MakeGarbageCollected<V8UnionBooleanOrString>(input.IdealString()));
}
return MakeGarbageCollected<
V8UnionBooleanOrConstrainBooleanOrDOMStringParametersOrString>(output);
}
return nullptr;
}
void ConvertConstraintSet(const MediaTrackConstraintSetPlatform& input,
NakedValueDisposition naked_treatment,
MediaTrackConstraintSet* output) {
if (!input.width.IsUnconstrained())
output->setWidth(ConvertLong(input.width, naked_treatment));
if (!input.height.IsUnconstrained())
output->setHeight(ConvertLong(input.height, naked_treatment));
if (!input.aspect_ratio.IsUnconstrained())
output->setAspectRatio(ConvertDouble(input.aspect_ratio, naked_treatment));
if (!input.frame_rate.IsUnconstrained())
output->setFrameRate(ConvertDouble(input.frame_rate, naked_treatment));
if (!input.facing_mode.IsUnconstrained())
output->setFacingMode(ConvertString(input.facing_mode, naked_treatment));
if (!input.resize_mode.IsUnconstrained())
output->setResizeMode(ConvertString(input.resize_mode, naked_treatment));
if (!input.sample_rate.IsUnconstrained())
output->setSampleRate(ConvertLong(input.sample_rate, naked_treatment));
if (!input.sample_size.IsUnconstrained())
output->setSampleSize(ConvertLong(input.sample_size, naked_treatment));
if (!input.echo_cancellation.IsUnconstrained()) {
output->setEchoCancellation(
ConvertBooleanOrString(input.echo_cancellation, naked_treatment));
}
if (!input.auto_gain_control.IsUnconstrained()) {
output->setAutoGainControl(
ConvertBoolean(input.auto_gain_control, naked_treatment));
}
if (!input.noise_suppression.IsUnconstrained()) {
output->setNoiseSuppression(
ConvertBoolean(input.noise_suppression, naked_treatment));
}
if (!input.voice_isolation.IsUnconstrained()) {
output->setVoiceIsolation(
ConvertBoolean(input.voice_isolation, naked_treatment));
}
if (!input.latency.IsUnconstrained())
output->setLatency(ConvertDouble(input.latency, naked_treatment));
if (!input.channel_count.IsUnconstrained())
output->setChannelCount(ConvertLong(input.channel_count, naked_treatment));
if (!input.device_id.IsUnconstrained())
output->setDeviceId(ConvertString(input.device_id, naked_treatment));
if (!input.group_id.IsUnconstrained())
output->setGroupId(ConvertString(input.group_id, naked_treatment));
if (!input.exposure_compensation.IsUnconstrained()) {
output->setExposureCompensation(
ConvertDouble(input.exposure_compensation, naked_treatment));
}
if (!input.exposure_time.IsUnconstrained()) {
output->setExposureTime(
ConvertDouble(input.exposure_time, naked_treatment));
}
if (!input.color_temperature.IsUnconstrained()) {
output->setColorTemperature(
ConvertDouble(input.color_temperature, naked_treatment));
}
if (!input.iso.IsUnconstrained()) {
output->setIso(ConvertDouble(input.iso, naked_treatment));
}
if (!input.brightness.IsUnconstrained()) {
output->setBrightness(ConvertDouble(input.brightness, naked_treatment));
}
if (!input.contrast.IsUnconstrained()) {
output->setContrast(ConvertDouble(input.contrast, naked_treatment));
}
if (!input.saturation.IsUnconstrained()) {
output->setSaturation(ConvertDouble(input.saturation, naked_treatment));
}
if (!input.sharpness.IsUnconstrained()) {
output->setSharpness(ConvertDouble(input.sharpness, naked_treatment));
}
if (!input.focus_distance.IsUnconstrained()) {
output->setFocusDistance(
ConvertDouble(input.focus_distance, naked_treatment));
}
if (!input.pan.IsUnconstrained())
output->setPan(ConvertBooleanOrDouble(input.pan, naked_treatment));
if (!input.tilt.IsUnconstrained())
output->setTilt(ConvertBooleanOrDouble(input.tilt, naked_treatment));
if (!input.zoom.IsUnconstrained())
output->setZoom(ConvertBooleanOrDouble(input.zoom, naked_treatment));
if (!input.torch.IsUnconstrained()) {
output->setTorch(ConvertBoolean(input.torch, naked_treatment));
}
if (!input.background_blur.IsUnconstrained()) {
output->setBackgroundBlur(
ConvertBoolean(input.background_blur, naked_treatment));
}
if (!input.background_segmentation_mask.IsUnconstrained()) {
output->setBackgroundSegmentationMask(
ConvertBoolean(input.background_segmentation_mask, naked_treatment));
}
if (!input.eye_gaze_correction.IsUnconstrained()) {
output->setEyeGazeCorrection(
ConvertBoolean(input.eye_gaze_correction, naked_treatment));
}
if (!input.face_framing.IsUnconstrained()) {
output->setFaceFraming(ConvertBoolean(input.face_framing, naked_treatment));
}
if (!input.suppress_local_audio_playback.IsUnconstrained()) {
output->setSuppressLocalAudioPlayback(
ConvertBoolean(input.suppress_local_audio_playback, naked_treatment));
}
if (!input.restrict_own_audio.IsUnconstrained()) {
output->setRestrictOwnAudio(
ConvertBoolean(input.restrict_own_audio, naked_treatment));
}
// TODO(hta): Decide the future of the nonstandard constraints.
// If they go forward, they need to be added here.
// https://crbug.com/605673
}
} // namespace
MediaConstraints ConvertTrackConstraintsToMediaConstraints(
const MediaTrackConstraints* constraints_in,
String& error_message) {
MediaTrackConstraintSetPlatform constraint_buffer;
Vector<MediaTrackConstraintSetPlatform> advanced_buffer;
if (!ValidateAndCopyConstraintSet(constraints_in,
NakedValueDisposition::kTreatAsIdeal,
constraint_buffer, error_message)) {
return MediaConstraints();
}
if (constraints_in->hasAdvanced()) {
for (const auto& element : constraints_in->advanced()) {
MediaTrackConstraintSetPlatform advanced_element;
if (!ValidateAndCopyConstraintSet(element,
NakedValueDisposition::kTreatAsExact,
advanced_element, error_message)) {
return MediaConstraints();
}
advanced_buffer.push_back(advanced_element);
}
}
MediaConstraints constraints;
constraints.Initialize(constraint_buffer, advanced_buffer);
return constraints;
}
MediaConstraints Create(ExecutionContext* context,
const MediaTrackConstraints* constraints_in,
String& error_message) {
MediaConstraints standard_form =
ConvertTrackConstraintsToMediaConstraints(constraints_in, error_message);
if (standard_form.IsNull()) {
return standard_form;
}
if (constraints_in->hasOptional() || constraints_in->hasMandatory()) {
if (!standard_form.IsUnconstrained()) {
UseCounter::Count(context, WebFeature::kMediaStreamConstraintsOldAndNew);
error_message =
"Malformed constraint: Cannot use both optional/mandatory and "
"specific or advanced constraints.";
return MediaConstraints();
}
Vector<NameValueStringConstraint> optional;
Vector<NameValueStringConstraint> mandatory;
if (!Parse(constraints_in, optional, mandatory)) {
error_message = "Malformed constraints object.";
return MediaConstraints();
}
UseCounter::Count(context, WebFeature::kMediaStreamConstraintsNameValue);
return CreateFromNamedConstraints(context, mandatory, optional);
}
UseCounter::Count(context, WebFeature::kMediaStreamConstraintsConformant);
return standard_form;
}
MediaConstraints Create() {
MediaConstraints constraints;
constraints.Initialize();
return constraints;
}
MediaTrackConstraints* ConvertConstraints(const MediaConstraints& input) {
MediaTrackConstraints* output = MediaTrackConstraints::Create();
if (input.IsNull())
return output;
ConvertConstraintSet(input.Basic(), NakedValueDisposition::kTreatAsIdeal,
output);
HeapVector<Member<MediaTrackConstraintSet>> advanced_vector;
for (const auto& it : input.Advanced()) {
if (it.IsUnconstrained())
continue;
MediaTrackConstraintSet* element = MediaTrackConstraintSet::Create();
ConvertConstraintSet(it, NakedValueDisposition::kTreatAsExact, element);
advanced_vector.push_back(element);
}
if (!advanced_vector.empty())
output->setAdvanced(advanced_vector);
return output;
}
} // namespace media_constraints_impl
} // namespace blink