| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * Copyright (C) 2016-2020 Apple 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. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(MEDIA_STREAM) |
| |
| #include "RealtimeMediaSourceSupportedConstraints.h" |
| #include <cstdlib> |
| #include <wtf/EnumTraits.h> |
| #include <wtf/Function.h> |
| #include <wtf/Vector.h> |
| |
| namespace WebCore { |
| |
| class MediaConstraint { |
| public: |
| enum class DataType : uint8_t { None, Integer, Double, Boolean, String }; |
| |
| bool isInt() const { return m_dataType == DataType::Integer; } |
| bool isDouble() const { return m_dataType == DataType::Double; } |
| bool isBoolean() const { return m_dataType == DataType::Boolean; } |
| bool isString() const { return m_dataType == DataType::String; } |
| |
| DataType dataType() const { return m_dataType; } |
| MediaConstraintType constraintType() const { return m_constraintType; } |
| const String& name() const { return m_name; } |
| |
| template <class Encoder> void encode(Encoder& encoder) const |
| { |
| encoder << m_constraintType; |
| encoder << m_name; |
| encoder << m_dataType; |
| } |
| |
| template <class Decoder> static WARN_UNUSED_RETURN bool decode(Decoder& decoder, MediaConstraint& constraint) |
| { |
| if (!decoder.decode(constraint.m_constraintType)) |
| return false; |
| |
| if (!decoder.decode(constraint.m_name)) |
| return false; |
| |
| if (!decoder.decode(constraint.m_dataType)) |
| return false; |
| |
| return true; |
| } |
| |
| void log() const; |
| |
| protected: |
| MediaConstraint(const String& name, MediaConstraintType constraintType, DataType dataType) |
| : m_name(name) |
| , m_constraintType(constraintType) |
| , m_dataType(dataType) |
| { |
| } |
| |
| MediaConstraint() = default; |
| ~MediaConstraint() = default; |
| |
| private: |
| String m_name; |
| MediaConstraintType m_constraintType { MediaConstraintType::Unknown }; |
| DataType m_dataType { DataType::None }; |
| }; |
| |
| template<class ValueType> |
| class NumericConstraint : public MediaConstraint { |
| public: |
| void setMin(ValueType value) { m_min = value; } |
| void setMax(ValueType value) { m_max = value; } |
| void setExact(ValueType value) { m_exact = value; } |
| void setIdeal(ValueType value) { m_ideal = value; } |
| |
| bool getMin(ValueType& min) const |
| { |
| if (!m_min) |
| return false; |
| |
| min = m_min.value(); |
| return true; |
| } |
| |
| bool getMax(ValueType& max) const |
| { |
| if (!m_max) |
| return false; |
| |
| max = m_max.value(); |
| return true; |
| } |
| |
| bool getExact(ValueType& exact) const |
| { |
| if (!m_exact) |
| return false; |
| |
| exact = m_exact.value(); |
| return true; |
| } |
| |
| bool getIdeal(ValueType& ideal) const |
| { |
| if (!m_ideal) |
| return false; |
| |
| ideal = m_ideal.value(); |
| return true; |
| } |
| |
| bool nearlyEqual(double a, double b) const |
| { |
| // Don't require strict equality when comparing constraints, or many floating point constraint values, |
| // e.g. "aspectRatio: 1.333", will never match. |
| const double epsilon = 0.00001; |
| return std::abs(a - b) <= epsilon; |
| } |
| |
| double fitnessDistance(ValueType rangeMin, ValueType rangeMax) const |
| { |
| // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints |
| // 1. If the constraint is not supported by the browser, the fitness distance is 0. |
| if (isEmpty()) |
| return 0; |
| |
| // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings |
| // dictionary's value for the constraint does not satisfy the constraint, the |
| // fitness distance is positive infinity. |
| bool valid = validForRange(rangeMin, rangeMax); |
| if (m_exact) { |
| if (valid && m_min && m_exact.value() < m_min.value()) |
| valid = false; |
| if (valid && m_max && m_exact.value() > m_max.value()) |
| valid = false; |
| if (!valid) |
| return std::numeric_limits<double>::infinity(); |
| } |
| |
| if (m_min) { |
| if (valid && m_max && m_min.value() > m_max.value()) |
| valid = false; |
| if (!valid) |
| return std::numeric_limits<double>::infinity(); |
| } |
| |
| if (m_max) { |
| if (valid && m_min && m_max.value() < m_min.value()) |
| valid = false; |
| if (!valid) |
| return std::numeric_limits<double>::infinity(); |
| } |
| |
| // 3. If no ideal value is specified, the fitness distance is 0. |
| if (!m_ideal) |
| return 0; |
| |
| // 4. For all positive numeric non-required constraints (such as height, width, frameRate, |
| // aspectRatio, sampleRate and sampleSize), the fitness distance is the result of the formula |
| // |
| // (actual == ideal) ? 0 : |actual - ideal| / max(|actual|,|ideal|) |
| ValueType ideal = m_ideal.value(); |
| if (ideal >= rangeMin && ideal <= rangeMax) |
| return 0; |
| |
| ideal = ideal > std::max(rangeMin, rangeMax) ? rangeMax : rangeMin; |
| return static_cast<double>(std::abs(ideal - m_ideal.value())) / std::max(std::abs(ideal), std::abs(m_ideal.value())); |
| } |
| |
| double fitnessDistance(const Vector<ValueType>& discreteCapabilityValues) const |
| { |
| double minDistance = std::numeric_limits<double>::infinity(); |
| |
| for (auto& value : discreteCapabilityValues) { |
| auto distance = fitnessDistance(value, value); |
| if (distance < minDistance) |
| minDistance = distance; |
| } |
| |
| return minDistance; |
| } |
| |
| bool validForRange(ValueType rangeMin, ValueType rangeMax) const |
| { |
| if (isEmpty()) |
| return false; |
| |
| if (m_exact) { |
| const ValueType exact = m_exact.value(); |
| if (exact < rangeMin && !nearlyEqual(exact, rangeMin)) |
| return false; |
| if (exact > rangeMax && !nearlyEqual(exact, rangeMax)) |
| return false; |
| } |
| |
| if (m_min) { |
| const ValueType constraintMin = m_min.value(); |
| if (constraintMin > rangeMax && !nearlyEqual(constraintMin, rangeMax)) |
| return false; |
| } |
| |
| if (m_max) { |
| const ValueType constraintMax = m_max.value(); |
| if (constraintMax < rangeMin && !nearlyEqual(constraintMax, rangeMin)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| ValueType find(const Function<bool(ValueType)>& function) const |
| { |
| if (m_min && function(m_min.value())) |
| return m_min.value(); |
| |
| if (m_max && function(m_max.value())) |
| return m_max.value(); |
| |
| if (m_exact && function(m_exact.value())) |
| return m_exact.value(); |
| |
| if (m_ideal && function(m_ideal.value())) |
| return m_ideal.value(); |
| |
| return 0; |
| } |
| |
| ValueType valueForCapabilityRange(ValueType current, ValueType capabilityMin, ValueType capabilityMax) const |
| { |
| ValueType value { 0 }; |
| ValueType min { capabilityMin }; |
| ValueType max { capabilityMax }; |
| |
| if (m_exact) { |
| ASSERT(validForRange(capabilityMin, capabilityMax)); |
| return m_exact.value(); |
| } |
| |
| if (m_min) { |
| value = m_min.value(); |
| ASSERT(validForRange(value, capabilityMax)); |
| if (value > min) |
| min = value; |
| if (value < min) |
| value = min; |
| |
| // If there is no ideal, don't change if minimum is smaller than current. |
| if (!m_ideal && value < current) |
| value = current; |
| } |
| |
| if (m_max) { |
| value = m_max.value(); |
| ASSERT(validForRange(capabilityMin, value)); |
| if (value < max) |
| max = value; |
| if (value > max) |
| value = max; |
| } |
| |
| if (m_ideal) |
| value = std::max(min, std::min(max, m_ideal.value())); |
| |
| return value; |
| } |
| |
| std::optional<ValueType> valueForDiscreteCapabilityValues(ValueType current, const Vector<ValueType>& discreteCapabilityValues) const |
| { |
| std::optional<ValueType> value; |
| std::optional<ValueType> min; |
| std::optional<ValueType> max; |
| |
| if (m_exact) { |
| ASSERT(discreteCapabilityValues.contains(m_exact.value())); |
| return m_exact.value(); |
| } |
| |
| if (m_min) { |
| auto index = discreteCapabilityValues.findMatching([&](ValueType value) { return m_min.value() >= value; }); |
| if (index != notFound) { |
| min = value = discreteCapabilityValues[index]; |
| |
| // If there is no ideal, don't change if minimum is smaller than current. |
| if (!m_ideal && *value < current) |
| value = current; |
| } |
| } |
| |
| if (m_max && m_max.value() >= discreteCapabilityValues[0]) { |
| for (auto& discreteValue : discreteCapabilityValues) { |
| if (m_max.value() <= discreteValue) |
| max = value = discreteValue; |
| } |
| } |
| |
| if (m_ideal && discreteCapabilityValues.contains(m_ideal.value())) { |
| value = m_ideal.value(); |
| if (max) |
| value = std::min(max.value(), *value); |
| if (min) |
| value = std::max(min.value(), *value); |
| } |
| |
| return value; |
| } |
| |
| bool isEmpty() const { return !m_min && !m_max && !m_exact && !m_ideal; } |
| bool isMandatory() const { return m_min || m_max || m_exact; } |
| |
| template <class Encoder> void encode(Encoder& encoder) const |
| { |
| MediaConstraint::encode(encoder); |
| |
| encoder << m_min; |
| encoder << m_max; |
| encoder << m_exact; |
| encoder << m_ideal; |
| } |
| |
| template <class Decoder> static WARN_UNUSED_RETURN bool decode(Decoder& decoder, NumericConstraint& constraint) |
| { |
| if (!MediaConstraint::decode(decoder, constraint)) |
| return false; |
| |
| if (!decoder.decode(constraint.m_min)) |
| return false; |
| if (!decoder.decode(constraint.m_max)) |
| return false; |
| if (!decoder.decode(constraint.m_exact)) |
| return false; |
| if (!decoder.decode(constraint.m_ideal)) |
| return false; |
| |
| return true; |
| } |
| |
| protected: |
| NumericConstraint(const String& name, MediaConstraintType type, DataType dataType) |
| : MediaConstraint(name, type, dataType) |
| { |
| } |
| |
| NumericConstraint() = default; |
| |
| void innerMerge(const NumericConstraint& other) |
| { |
| if (other.isEmpty()) |
| return; |
| |
| ValueType value; |
| if (other.getExact(value)) |
| m_exact = value; |
| |
| if (other.getMin(value)) |
| m_min = value; |
| |
| if (other.getMax(value)) |
| m_max = value; |
| |
| // https://w3c.github.io/mediacapture-main/#constrainable-interface |
| // When processing advanced constraints: |
| // ... the User Agent must attempt to apply, individually, any 'ideal' constraints or |
| // a constraint given as a bare value for the property. Of these properties, it must |
| // satisfy the largest number that it can, in any order. |
| if (other.getIdeal(value)) { |
| if (!m_ideal || value > m_ideal.value()) |
| m_ideal = value; |
| } |
| } |
| |
| std::optional<ValueType> m_min; |
| std::optional<ValueType> m_max; |
| std::optional<ValueType> m_exact; |
| std::optional<ValueType> m_ideal; |
| }; |
| |
| class IntConstraint final : public NumericConstraint<int> { |
| public: |
| IntConstraint(const String& name, MediaConstraintType type) |
| : NumericConstraint<int>(name, type, DataType::Integer) |
| { |
| } |
| |
| IntConstraint() = default; |
| |
| void merge(const MediaConstraint& other) |
| { |
| ASSERT(other.isInt()); |
| NumericConstraint::innerMerge(downcast<const IntConstraint>(other)); |
| } |
| |
| void logAsInt() const; |
| }; |
| |
| class DoubleConstraint final : public NumericConstraint<double> { |
| public: |
| DoubleConstraint(const String& name, MediaConstraintType type) |
| : NumericConstraint<double>(name, type, DataType::Double) |
| { |
| } |
| |
| DoubleConstraint() = default; |
| |
| void merge(const MediaConstraint& other) |
| { |
| ASSERT(other.isDouble()); |
| NumericConstraint::innerMerge(downcast<DoubleConstraint>(other)); |
| } |
| |
| void logAsDouble() const; |
| }; |
| |
| class BooleanConstraint final : public MediaConstraint { |
| public: |
| BooleanConstraint(const String& name, MediaConstraintType type) |
| : MediaConstraint(name, type, DataType::Boolean) |
| { |
| } |
| |
| BooleanConstraint() = default; |
| |
| void setExact(bool value) { m_exact = value; } |
| void setIdeal(bool value) { m_ideal = value; } |
| |
| bool getExact(bool& exact) const |
| { |
| if (!m_exact) |
| return false; |
| |
| exact = m_exact.value(); |
| return true; |
| } |
| |
| bool getIdeal(bool& ideal) const |
| { |
| if (!m_ideal) |
| return false; |
| |
| ideal = m_ideal.value(); |
| return true; |
| } |
| |
| double fitnessDistance(bool value) const |
| { |
| // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints |
| // 1. If the constraint is not supported by the browser, the fitness distance is 0. |
| if (isEmpty()) |
| return 0; |
| |
| // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings |
| // dictionary's value for the constraint does not satisfy the constraint, the |
| // fitness distance is positive infinity. |
| if (m_exact && value != m_exact.value()) |
| return std::numeric_limits<double>::infinity(); |
| |
| // 3. If no ideal value is specified, the fitness distance is 0. |
| if (!m_ideal || m_ideal.value() == value) |
| return 0; |
| |
| // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode, |
| // echoCancellation), the fitness distance is the result of the formula |
| // (actual == ideal) ? 0 : 1 |
| return 1; |
| } |
| |
| void merge(const MediaConstraint& other) |
| { |
| ASSERT(other.isBoolean()); |
| const BooleanConstraint& typedOther = downcast<BooleanConstraint>(other); |
| |
| if (typedOther.isEmpty()) |
| return; |
| |
| bool value; |
| if (typedOther.getExact(value)) |
| m_exact = value; |
| |
| if (typedOther.getIdeal(value)) { |
| if (!m_ideal || (value && !m_ideal.value())) |
| m_ideal = value; |
| } |
| } |
| |
| bool isEmpty() const { return !m_exact && !m_ideal; }; |
| bool isMandatory() const { return bool(m_exact); } |
| |
| template <class Encoder> void encode(Encoder& encoder) const |
| { |
| MediaConstraint::encode(encoder); |
| encoder << m_exact; |
| encoder << m_ideal; |
| } |
| |
| template <class Decoder> static WARN_UNUSED_RETURN bool decode(Decoder& decoder, BooleanConstraint& constraint) |
| { |
| if (!MediaConstraint::decode(decoder, constraint)) |
| return false; |
| |
| if (!decoder.decode(constraint.m_exact)) |
| return false; |
| if (!decoder.decode(constraint.m_ideal)) |
| return false; |
| |
| return true; |
| } |
| |
| void logAsBoolean() const; |
| |
| private: |
| std::optional<bool> m_exact; |
| std::optional<bool> m_ideal; |
| }; |
| |
| class StringConstraint : public MediaConstraint { |
| public: |
| StringConstraint(const String& name, MediaConstraintType type) |
| : MediaConstraint(name, type, DataType::String) |
| { |
| } |
| |
| StringConstraint() = default; |
| |
| void setExact(const String& value) |
| { |
| m_exact.clear(); |
| m_exact.append(value); |
| } |
| |
| void appendExact(const String& value) |
| { |
| m_exact.append(value); |
| } |
| |
| void setIdeal(const String& value) |
| { |
| m_ideal.clear(); |
| m_ideal.append(value); |
| } |
| |
| void appendIdeal(const String& value) |
| { |
| m_ideal.append(value); |
| } |
| |
| bool getExact(Vector<String>& exact) const |
| { |
| if (!m_exact.isEmpty()) |
| return false; |
| |
| exact = m_exact; |
| return true; |
| } |
| |
| bool getIdeal(Vector<String>& ideal) const |
| { |
| if (!m_ideal.isEmpty()) |
| return false; |
| |
| ideal = m_ideal; |
| return true; |
| } |
| |
| double fitnessDistance(const String&) const; |
| double fitnessDistance(const Vector<String>&) const; |
| |
| const String& find(const Function<bool(const String&)>&) const; |
| |
| bool isEmpty() const { return m_exact.isEmpty() && m_ideal.isEmpty(); } |
| bool isMandatory() const { return !m_exact.isEmpty(); } |
| WEBCORE_EXPORT void merge(const MediaConstraint&); |
| |
| template <class Encoder> void encode(Encoder& encoder) const |
| { |
| MediaConstraint::encode(encoder); |
| |
| encoder << m_exact; |
| encoder << m_ideal; |
| } |
| |
| template <class Decoder> static WARN_UNUSED_RETURN bool decode(Decoder& decoder, StringConstraint& constraint) |
| { |
| if (!MediaConstraint::decode(decoder, constraint)) |
| return false; |
| |
| if (!decoder.decode(constraint.m_exact)) |
| return false; |
| if (!decoder.decode(constraint.m_ideal)) |
| return false; |
| |
| return true; |
| } |
| |
| void removeEmptyStringConstraint() |
| { |
| m_exact.removeAllMatching([](auto& constraint) { |
| return constraint.isEmpty(); |
| }); |
| m_ideal.removeAllMatching([](auto& constraint) { |
| return constraint.isEmpty(); |
| }); |
| } |
| |
| private: |
| Vector<String> m_exact; |
| Vector<String> m_ideal; |
| }; |
| |
| class UnknownConstraint final : public MediaConstraint { |
| public: |
| UnknownConstraint(const String& name, MediaConstraintType type) |
| : MediaConstraint(name, type, DataType::None) |
| { |
| } |
| |
| private: |
| bool isEmpty() const { return true; } |
| bool isMandatory() const { return false; } |
| void merge(const MediaConstraint&) { } |
| }; |
| |
| class MediaTrackConstraintSetMap { |
| public: |
| WEBCORE_EXPORT void forEach(Function<void(const MediaConstraint&)>&&) const; |
| void filter(const Function<bool(const MediaConstraint&)>&) const; |
| bool isEmpty() const; |
| WEBCORE_EXPORT size_t size() const; |
| |
| WEBCORE_EXPORT void set(MediaConstraintType, std::optional<IntConstraint>&&); |
| WEBCORE_EXPORT void set(MediaConstraintType, std::optional<DoubleConstraint>&&); |
| WEBCORE_EXPORT void set(MediaConstraintType, std::optional<BooleanConstraint>&&); |
| WEBCORE_EXPORT void set(MediaConstraintType, std::optional<StringConstraint>&&); |
| |
| std::optional<IntConstraint> width() const { return m_width; } |
| std::optional<IntConstraint> height() const { return m_height; } |
| std::optional<IntConstraint> sampleRate() const { return m_sampleRate; } |
| std::optional<IntConstraint> sampleSize() const { return m_sampleSize; } |
| |
| std::optional<DoubleConstraint> aspectRatio() const { return m_aspectRatio; } |
| std::optional<DoubleConstraint> frameRate() const { return m_frameRate; } |
| std::optional<DoubleConstraint> volume() const { return m_volume; } |
| |
| std::optional<BooleanConstraint> echoCancellation() const { return m_echoCancellation; } |
| std::optional<BooleanConstraint> displaySurface() const { return m_displaySurface; } |
| std::optional<BooleanConstraint> logicalSurface() const { return m_logicalSurface; } |
| |
| std::optional<StringConstraint> facingMode() const { return m_facingMode; } |
| std::optional<StringConstraint> deviceId() const { return m_deviceId; } |
| std::optional<StringConstraint> groupId() const { return m_groupId; } |
| |
| template <class Encoder> void encode(Encoder& encoder) const |
| { |
| encoder << m_width; |
| encoder << m_height; |
| encoder << m_sampleRate; |
| encoder << m_sampleSize; |
| |
| encoder << m_aspectRatio; |
| encoder << m_frameRate; |
| encoder << m_volume; |
| |
| encoder << m_echoCancellation; |
| encoder << m_displaySurface; |
| encoder << m_logicalSurface; |
| |
| encoder << m_facingMode; |
| encoder << m_deviceId; |
| encoder << m_groupId; |
| } |
| |
| template <class Decoder> static std::optional<MediaTrackConstraintSetMap> decode(Decoder& decoder) |
| { |
| MediaTrackConstraintSetMap map; |
| if (!decoder.decode(map.m_width)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_height)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_sampleRate)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_sampleSize)) |
| return std::nullopt; |
| |
| if (!decoder.decode(map.m_aspectRatio)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_frameRate)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_volume)) |
| return std::nullopt; |
| |
| if (!decoder.decode(map.m_echoCancellation)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_displaySurface)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_logicalSurface)) |
| return std::nullopt; |
| |
| if (!decoder.decode(map.m_facingMode)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_deviceId)) |
| return std::nullopt; |
| if (!decoder.decode(map.m_groupId)) |
| return std::nullopt; |
| |
| return map; |
| } |
| |
| private: |
| std::optional<IntConstraint> m_width; |
| std::optional<IntConstraint> m_height; |
| std::optional<IntConstraint> m_sampleRate; |
| std::optional<IntConstraint> m_sampleSize; |
| |
| std::optional<DoubleConstraint> m_aspectRatio; |
| std::optional<DoubleConstraint> m_frameRate; |
| std::optional<DoubleConstraint> m_volume; |
| |
| std::optional<BooleanConstraint> m_echoCancellation; |
| std::optional<BooleanConstraint> m_displaySurface; |
| std::optional<BooleanConstraint> m_logicalSurface; |
| |
| std::optional<StringConstraint> m_facingMode; |
| std::optional<StringConstraint> m_deviceId; |
| std::optional<StringConstraint> m_groupId; |
| }; |
| |
| class FlattenedConstraint { |
| public: |
| |
| void set(const MediaConstraint&); |
| void merge(const MediaConstraint&); |
| void append(const MediaConstraint&); |
| const MediaConstraint* find(MediaConstraintType) const; |
| bool isEmpty() const { return m_variants.isEmpty(); } |
| |
| class iterator { |
| public: |
| iterator(const FlattenedConstraint* constraint, size_t index) |
| : m_constraint(constraint) |
| , m_index(index) |
| #ifndef NDEBUG |
| , m_generation(constraint->m_generation) |
| #endif |
| { |
| } |
| |
| MediaConstraint& operator*() const |
| { |
| return m_constraint->m_variants.at(m_index).constraint(); |
| } |
| |
| iterator& operator++() |
| { |
| #ifndef NDEBUG |
| ASSERT(m_generation == m_constraint->m_generation); |
| #endif |
| m_index++; |
| return *this; |
| } |
| |
| bool operator==(const iterator& other) const { return m_index == other.m_index; } |
| bool operator!=(const iterator& other) const { return !(*this == other); } |
| |
| private: |
| const FlattenedConstraint* m_constraint { nullptr }; |
| size_t m_index { 0 }; |
| #ifndef NDEBUG |
| int m_generation { 0 }; |
| #endif |
| }; |
| |
| const iterator begin() const { return iterator(this, 0); } |
| const iterator end() const { return iterator(this, m_variants.size()); } |
| |
| private: |
| class ConstraintHolder { |
| public: |
| static ConstraintHolder create(const MediaConstraint& value) { return ConstraintHolder(value); } |
| |
| ~ConstraintHolder() |
| { |
| if (m_value.asRaw) { |
| switch (dataType()) { |
| case MediaConstraint::DataType::Integer: |
| delete m_value.asInteger; |
| break; |
| case MediaConstraint::DataType::Double: |
| delete m_value.asDouble; |
| break; |
| case MediaConstraint::DataType::Boolean: |
| delete m_value.asBoolean; |
| break; |
| case MediaConstraint::DataType::String: |
| delete m_value.asString; |
| break; |
| case MediaConstraint::DataType::None: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| #ifndef NDEBUG |
| m_value.asRaw = reinterpret_cast<MediaConstraint*>(0xbbadbeef); |
| #endif |
| } |
| |
| ConstraintHolder(ConstraintHolder&& other) |
| { |
| switch (other.dataType()) { |
| case MediaConstraint::DataType::Integer: |
| m_value.asInteger = std::exchange(other.m_value.asInteger, nullptr); |
| break; |
| case MediaConstraint::DataType::Double: |
| m_value.asDouble = std::exchange(other.m_value.asDouble, nullptr); |
| break; |
| case MediaConstraint::DataType::Boolean: |
| m_value.asBoolean = std::exchange(other.m_value.asBoolean, nullptr); |
| break; |
| case MediaConstraint::DataType::String: |
| m_value.asString = std::exchange(other.m_value.asString, nullptr); |
| break; |
| case MediaConstraint::DataType::None: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| |
| MediaConstraint& constraint() const { return *m_value.asRaw; } |
| MediaConstraint::DataType dataType() const { return constraint().dataType(); } |
| MediaConstraintType constraintType() const { return constraint().constraintType(); } |
| |
| private: |
| explicit ConstraintHolder(const MediaConstraint& value) |
| { |
| switch (value.dataType()) { |
| case MediaConstraint::DataType::Integer: |
| m_value.asInteger = new IntConstraint(downcast<const IntConstraint>(value)); |
| break; |
| case MediaConstraint::DataType::Double: |
| m_value.asDouble = new DoubleConstraint(downcast<DoubleConstraint>(value)); |
| break; |
| case MediaConstraint::DataType::Boolean: |
| m_value.asBoolean = new BooleanConstraint(downcast<BooleanConstraint>(value)); |
| break; |
| case MediaConstraint::DataType::String: |
| m_value.asString = new StringConstraint(downcast<StringConstraint>(value)); |
| break; |
| case MediaConstraint::DataType::None: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| |
| union { |
| MediaConstraint* asRaw; |
| IntConstraint* asInteger; |
| DoubleConstraint* asDouble; |
| BooleanConstraint* asBoolean; |
| StringConstraint* asString; |
| } m_value; |
| }; |
| |
| Vector<ConstraintHolder> m_variants; |
| #ifndef NDEBUG |
| int m_generation { 0 }; |
| #endif |
| }; |
| |
| struct MediaConstraints { |
| void setDefaultVideoConstraints(); |
| bool isConstraintSet(const Function<bool(const MediaTrackConstraintSetMap&)>&); |
| |
| MediaTrackConstraintSetMap mandatoryConstraints; |
| Vector<MediaTrackConstraintSetMap> advancedConstraints; |
| bool isValid { false }; |
| }; |
| |
| } // namespace WebCore |
| |
| #define SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(ConstraintType, predicate) \ |
| SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ConstraintType) \ |
| static bool isType(const WebCore::MediaConstraint& constraint) { return constraint.predicate; } \ |
| SPECIALIZE_TYPE_TRAITS_END() |
| |
| SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(IntConstraint, isInt()) |
| SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(DoubleConstraint, isDouble()) |
| SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(StringConstraint, isString()) |
| SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(BooleanConstraint, isBoolean()) |
| |
| namespace WTF { |
| |
| template<> struct EnumTraits<WebCore::MediaConstraint::DataType> { |
| using values = EnumValues< |
| WebCore::MediaConstraint::DataType, |
| WebCore::MediaConstraint::DataType::None, |
| WebCore::MediaConstraint::DataType::Integer, |
| WebCore::MediaConstraint::DataType::Double, |
| WebCore::MediaConstraint::DataType::Boolean, |
| WebCore::MediaConstraint::DataType::String |
| >; |
| }; |
| |
| } // namespace WTF |
| |
| #endif // ENABLE(MEDIA_STREAM) |