Changeset 205574 in webkit


Ignore:
Timestamp:
Sep 7, 2016 5:01:29 PM (8 years ago)
Author:
eric.carlson@apple.com
Message:

[MediaStream] applyConstraints pt. 2 - advanced constraints
https://bugs.webkit.org/show_bug.cgi?id=161715
<rdar://problem/28195461>

Reviewed by Dean Jackson.

Source/WebCore:

Test: fast/mediastream/apply-constraints-advanced.html

  • platform/mediastream/MediaConstraints.cpp:

(WebCore::MediaConstraint::create): Return Ref<>, not RefPtr<>.
(WebCore::MediaConstraint::copy): New
(WebCore::IntConstraint::copy): Ditto.
(WebCore::DoubleConstraint::copy): Ditto.
(WebCore::BooleanConstraint::copy): Ditto.
(WebCore::StringConstraint::copy): Ditto.
(WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the

constraint and the specified value.

(WebCore::StringConstraint::merge): New, merge value into constraint.
(WebCore::FlattenedConstraint::set): New, add or replace a constraint.
(WebCore::FlattenedConstraint::merge): New, merge or add a constraint.

  • platform/mediastream/MediaConstraints.h:

(WebCore::MediaConstraint::fitnessDistance):
(WebCore::MediaConstraint::merge):
(WebCore::NumericConstraint::nearlyEqual):
(WebCore::FlattenedConstraint::isEmpty):
(WebCore::FlattenedConstraint::begin):
(WebCore::FlattenedConstraint::end):
(WebCore::MediaConstraint::~MediaConstraint): Deleted.
(WebCore::MediaConstraint::find): Deleted.
(WebCore::MediaConstraint::getIdeal): Deleted.

  • platform/mediastream/RealtimeMediaSource.cpp:

(WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source

capabilities and a constraint.

(WebCore::applyNumericConstraint): New, apply a numeric constraint.
(WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
(WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
(WebCore::RealtimeMediaSource::applyConstraints):
(WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
(WebCore::value): Deleted.

  • platform/mediastream/RealtimeMediaSource.h:

LayoutTests:

  • fast/mediastream/apply-constraints-advanced-expected.txt: Added.
  • fast/mediastream/apply-constraints-advanced.html: Added.
Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r205572 r205574  
     12016-09-07  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [MediaStream] applyConstraints pt. 2 - advanced constraints
     4        https://bugs.webkit.org/show_bug.cgi?id=161715
     5        <rdar://problem/28195461>
     6
     7        Reviewed by Dean Jackson.
     8
     9        * fast/mediastream/apply-constraints-advanced-expected.txt: Added.
     10        * fast/mediastream/apply-constraints-advanced.html: Added.
     11
    1122016-09-06  Dean Jackson  <dino@apple.com>
    213
  • trunk/Source/WebCore/ChangeLog

    r205569 r205574  
     12016-09-07  Eric Carlson  <eric.carlson@apple.com>
     2
     3        [MediaStream] applyConstraints pt. 2 - advanced constraints
     4        https://bugs.webkit.org/show_bug.cgi?id=161715
     5        <rdar://problem/28195461>
     6
     7        Reviewed by Dean Jackson.
     8
     9        Test: fast/mediastream/apply-constraints-advanced.html
     10
     11        * platform/mediastream/MediaConstraints.cpp:
     12        (WebCore::MediaConstraint::create): Return Ref<>, not RefPtr<>.
     13        (WebCore::MediaConstraint::copy): New
     14        (WebCore::IntConstraint::copy): Ditto.
     15        (WebCore::DoubleConstraint::copy): Ditto.
     16        (WebCore::BooleanConstraint::copy): Ditto.
     17        (WebCore::StringConstraint::copy): Ditto.
     18        (WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the
     19          constraint and the specified value.
     20        (WebCore::StringConstraint::merge): New, merge value into constraint.
     21        (WebCore::FlattenedConstraint::set): New, add or replace a constraint.
     22        (WebCore::FlattenedConstraint::merge): New, merge or add a constraint.
     23        * platform/mediastream/MediaConstraints.h:
     24        (WebCore::MediaConstraint::fitnessDistance):
     25        (WebCore::MediaConstraint::merge):
     26        (WebCore::NumericConstraint::nearlyEqual):
     27        (WebCore::FlattenedConstraint::isEmpty):
     28        (WebCore::FlattenedConstraint::begin):
     29        (WebCore::FlattenedConstraint::end):
     30        (WebCore::MediaConstraint::~MediaConstraint): Deleted.
     31        (WebCore::MediaConstraint::find): Deleted.
     32        (WebCore::MediaConstraint::getIdeal): Deleted.
     33
     34        * platform/mediastream/RealtimeMediaSource.cpp:
     35        (WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source
     36          capabilities and a constraint.
     37        (WebCore::applyNumericConstraint): New, apply a numeric constraint.
     38        (WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
     39        (WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
     40        (WebCore::RealtimeMediaSource::applyConstraints):
     41        (WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
     42        (WebCore::value): Deleted.
     43        * platform/mediastream/RealtimeMediaSource.h:
     44
    1452016-09-07  Mark Lam  <mark.lam@apple.com>
    246
  • trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp

    r205348 r205574  
    3838namespace WebCore {
    3939
    40 RefPtr<MediaConstraint> MediaConstraint::create(const String& name)
     40Ref<MediaConstraint> MediaConstraint::create(const String& name)
    4141{
    4242    MediaConstraintType constraintType = RealtimeMediaSourceSupportedConstraints::constraintFromName(name);
     
    6363}
    6464
     65Ref<MediaConstraint> MediaConstraint::copy() const
     66{
     67    return MediaConstraint::create(name());
     68}
     69
     70Ref<MediaConstraint> IntConstraint::copy() const
     71{
     72    auto copy = IntConstraint::create(name(), type());
     73    copy->m_min = m_min;
     74    copy->m_max = m_max;
     75    copy->m_exact = m_exact;
     76    copy->m_ideal = m_ideal;
     77
     78    return copy.leakRef();
     79}
     80
     81Ref<MediaConstraint> DoubleConstraint::copy() const
     82{
     83    auto copy = DoubleConstraint::create(name(), type());
     84    copy->m_min = m_min;
     85    copy->m_max = m_max;
     86    copy->m_exact = m_exact;
     87    copy->m_ideal = m_ideal;
     88   
     89    return copy.leakRef();
     90}
     91
     92Ref<MediaConstraint> BooleanConstraint::copy() const
     93{
     94    auto copy = BooleanConstraint::create(name(), type());
     95    copy->m_exact = m_exact;
     96    copy->m_ideal = m_ideal;
     97
     98    return copy.leakRef();
     99}
     100
     101Ref<MediaConstraint> StringConstraint::copy() const
     102{
     103    auto copy = StringConstraint::create(name(), type());
     104    copy->m_exact = m_exact;
     105    copy->m_ideal = m_ideal;
     106
     107    return copy.leakRef();
     108}
     109
    65110bool BooleanConstraint::getExact(bool& exact) const
    66111{
     
    137182}
    138183
     184double StringConstraint::fitnessDistance(const String& value) const
     185{
     186    // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
     187
     188    // 1. If the constraint is not supported by the browser, the fitness distance is 0.
     189    if (isEmpty())
     190        return 0;
     191
     192    // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
     193    //    dictionary's value for the constraint does not satisfy the constraint, the
     194    //    fitness distance is positive infinity.
     195    if (!m_exact.isEmpty() && m_exact.find(value) == notFound)
     196        return std::numeric_limits<double>::infinity();
     197
     198    // 3. If no ideal value is specified, the fitness distance is 0.
     199    if (m_exact.isEmpty())
     200        return 0;
     201
     202    // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
     203    // echoCancellation), the fitness distance is the result of the formula
     204    //        (actual == ideal) ? 0 : 1
     205    return m_ideal.find(value) != notFound ? 0 : 1;
     206}
     207
     208double StringConstraint::fitnessDistance(const Vector<String>& values) const
     209{
     210    if (isEmpty())
     211        return 0;
     212
     213    double minimumDistance = std::numeric_limits<double>::infinity();
     214    for (auto& value : values)
     215        minimumDistance = std::min(minimumDistance, fitnessDistance(value));
     216
     217    return minimumDistance;
     218}
     219
     220void StringConstraint::merge(const MediaConstraint& other)
     221{
     222    if (other.isEmpty())
     223        return;
     224
     225    Vector<String> values;
     226    if (other.getExact(values)) {
     227        if (m_exact.isEmpty())
     228            m_exact = values;
     229        else {
     230            for (auto& value : values) {
     231                if (m_exact.find(value) == notFound)
     232                    m_exact.append(value);
     233            }
     234        }
     235    }
     236
     237    if (other.getIdeal(values)) {
     238        if (m_ideal.isEmpty())
     239            m_ideal = values;
     240        else {
     241            for (auto& value : values) {
     242                if (m_ideal.find(value) == notFound)
     243                    m_ideal.append(value);
     244            }
     245        }
     246    }
     247}
     248
     249void FlattenedConstraint::set(const MediaConstraint& constraint)
     250{
     251    for (auto existingConstraint : m_constraints) {
     252        if (existingConstraint->type() == constraint.type()) {
     253            existingConstraint = constraint.copy();
     254            return;
     255        }
     256    }
     257
     258    m_constraints.append(constraint.copy());
     259}
     260
     261void FlattenedConstraint::merge(const MediaConstraint& constraint)
     262{
     263    for (auto existingConstraint : m_constraints) {
     264        if (existingConstraint->type() == constraint.type()) {
     265            existingConstraint->merge(constraint);
     266            return;
     267        }
     268    }
     269
     270    m_constraints.append(constraint.copy());
     271}
     272
    139273}
    140274
  • trunk/Source/WebCore/platform/mediastream/MediaConstraints.h

    r205348 r205574  
    3636
    3737#include "RealtimeMediaSourceSupportedConstraints.h"
     38#include <cstdlib>
    3839#include <wtf/HashMap.h>
    3940#include <wtf/RefCounted.h>
     41#include <wtf/text/StringHash.h>
    4042#include <wtf/text/WTFString.h>
    4143
     
    4446class MediaConstraint : public RefCounted<MediaConstraint> {
    4547public:
    46     static RefPtr<MediaConstraint> create(const String& name);
     48    static Ref<MediaConstraint> create(const String&);
    4749
    4850    enum class ConstraintType { ExactConstraint, IdealConstraint, MinConstraint, MaxConstraint };
    4951
    5052    virtual ~MediaConstraint() { };
     53
     54    virtual Ref<MediaConstraint> copy() const;
    5155    virtual bool isEmpty() const = 0;
    5256    virtual bool isMandatory() const = 0;
     
    5862    virtual bool validForRange(int, int) const { ASSERT_NOT_REACHED(); return false; }
    5963    virtual int find(std::function<bool(ConstraintType, int)>) const { ASSERT_NOT_REACHED(); return 0; }
     64    virtual double fitnessDistance(int, int) const { ASSERT_NOT_REACHED(); return 0; }
    6065
    6166    virtual bool getMin(double&) const { ASSERT_NOT_REACHED(); return false; }
     
    6570    virtual bool validForRange(double, double) const { ASSERT_NOT_REACHED(); return false; }
    6671    virtual double find(std::function<bool(ConstraintType, double)>) const { ASSERT_NOT_REACHED(); return 0; }
     72    virtual double fitnessDistance(double, double) const { ASSERT_NOT_REACHED(); return 0; }
    6773
    6874    virtual bool getMin(bool&) const { ASSERT_NOT_REACHED(); return false; }
     
    7076    virtual bool getExact(bool&) const { ASSERT_NOT_REACHED(); return false; }
    7177    virtual bool getIdeal(bool&) const { ASSERT_NOT_REACHED(); return false; }
     78    virtual double fitnessDistance(bool) const { ASSERT_NOT_REACHED(); return 0; }
    7279
    7380    virtual bool getMin(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
     
    7784    virtual const String& find(std::function<bool(ConstraintType, const String&)>) const { ASSERT_NOT_REACHED(); return emptyString(); }
    7885
     86    virtual double fitnessDistance(const String&) const { ASSERT_NOT_REACHED(); return 0; }
     87    virtual double fitnessDistance(const Vector<String>&) const { ASSERT_NOT_REACHED(); return 0; }
     88
     89    virtual void merge(const MediaConstraint&) { ASSERT_NOT_REACHED(); }
     90
    7991    MediaConstraintType type() const { return m_type; }
    8092    const String& name() const { return m_name; }
     
    135147    }
    136148
     149    bool nearlyEqual(double a, double b) const
     150    {
     151        // Don't require strict equality when comparing constraints, or many floating point constraint values,
     152        // e.g. "aspectRatio: 1.333", will never match.
     153        const double epsilon = 0.00001;
     154        return std::abs(a - b) <= epsilon;
     155    }
     156
     157    double fitnessDistance(ValueType rangeMin, ValueType rangeMax) const final {
     158        // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
     159        // 1. If the constraint is not supported by the browser, the fitness distance is 0.
     160        if (isEmpty())
     161            return 0;
     162
     163        // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
     164        //    dictionary's value for the constraint does not satisfy the constraint, the
     165        //    fitness distance is positive infinity.
     166        bool valid = validForRange(rangeMin, rangeMax);
     167        if (m_exact && !valid)
     168            return std::numeric_limits<double>::infinity();
     169
     170        if (m_min && !valid)
     171            return std::numeric_limits<double>::infinity();
     172
     173        if (m_max && !valid)
     174            return std::numeric_limits<double>::infinity();
     175
     176        // 3. If no ideal value is specified, the fitness distance is 0.
     177        if (!m_ideal)
     178            return 0;
     179
     180        // 4. For all positive numeric non-required constraints (such as height, width, frameRate,
     181        //    aspectRatio, sampleRate and sampleSize), the fitness distance is the result of the formula
     182        //
     183        //         (actual == ideal) ? 0 : |actual - ideal| / max(|actual|,|ideal|)
     184        ValueType ideal = m_ideal.value();
     185        if (ideal >= rangeMin && ideal <= rangeMax)
     186            return 0;
     187
     188        ideal = ideal > std::max(rangeMin, rangeMax) ? rangeMax : rangeMin;
     189        return static_cast<double>(std::abs(ideal - m_ideal.value())) / std::max(std::abs(ideal), std::abs(m_ideal.value()));
     190    }
     191
     192    void merge(const MediaConstraint& other) final {
     193        if (other.isEmpty())
     194            return;
     195
     196        ValueType value;
     197        if (other.getExact(value))
     198            m_exact = value;
     199
     200        if (other.getMin(value))
     201            m_min = value;
     202
     203        if (other.getMax(value))
     204            m_max = value;
     205
     206        // https://w3c.github.io/mediacapture-main/#constrainable-interface
     207        // When processing advanced constraints:
     208        //   ... the User Agent must attempt to apply, individually, any 'ideal' constraints or
     209        //   a constraint given as a bare value for the property. Of these properties, it must
     210        //   satisfy the largest number that it can, in any order.
     211        if (other.getIdeal(value)) {
     212            if (!m_ideal || value > m_ideal.value())
     213                m_ideal = value;
     214        }
     215    }
     216
    137217    bool validForRange(ValueType rangeMin, ValueType rangeMax) const final {
    138218        if (isEmpty())
    139219            return false;
    140220
    141         if (m_exact && (m_exact.value() < rangeMin || m_exact.value() > rangeMax))
    142             return false;
    143 
    144         if (m_min && m_min.value() > rangeMax)
    145             return false;
    146 
    147         if (m_max && m_max.value() < rangeMin)
    148             return false;
     221        if (m_exact) {
     222            const ValueType exact = m_exact.value();
     223            if (exact < rangeMin && !nearlyEqual(exact, rangeMin))
     224                return false;
     225            if (exact > rangeMax && !nearlyEqual(exact, rangeMax))
     226                return false;
     227        }
     228
     229        if (m_min) {
     230            const ValueType constraintMin = m_min.value();
     231            if (constraintMin > rangeMax && !nearlyEqual(constraintMin, rangeMax))
     232                return false;
     233        }
     234
     235
     236        if (m_max) {
     237            const ValueType constraintMax = m_max.value();
     238            if (constraintMax < rangeMin && !nearlyEqual(constraintMax, rangeMin))
     239                return false;
     240        }
    149241
    150242        return true;
     
    174266    }
    175267
    176 private:
    177268    Optional<ValueType> m_min;
    178269    Optional<ValueType> m_max;
     
    185276    static Ref<IntConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new IntConstraint(name, type)); }
    186277
     278    Ref<MediaConstraint> copy() const final;
     279
    187280private:
    188281    explicit IntConstraint(const String& name, MediaConstraintType type)
     
    196289    static Ref<DoubleConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new DoubleConstraint(name, type)); }
    197290
     291    Ref<MediaConstraint> copy() const final;
     292
    198293private:
    199294    explicit DoubleConstraint(const String& name, MediaConstraintType type)
     
    207302    static Ref<BooleanConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new BooleanConstraint(name, type)); }
    208303
     304    Ref<MediaConstraint> copy() const final;
     305
    209306    void setExact(bool value) { m_exact = value; }
    210307    void setIdeal(bool value) { m_ideal = value; }
     
    216313    bool isMandatory() const final { return bool(m_exact); }
    217314
     315    double fitnessDistance(bool value) const final {
     316        // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
     317        // 1. If the constraint is not supported by the browser, the fitness distance is 0.
     318        if (isEmpty())
     319            return 0;
     320
     321        // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
     322        //    dictionary's value for the constraint does not satisfy the constraint, the
     323        //    fitness distance is positive infinity.
     324        if (m_exact && value != m_exact.value())
     325            return std::numeric_limits<double>::infinity();
     326
     327        // 3. If no ideal value is specified, the fitness distance is 0.
     328        if (!m_ideal || m_ideal.value() == value)
     329            return 0;
     330
     331        // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
     332        // echoCancellation), the fitness distance is the result of the formula
     333        //        (actual == ideal) ? 0 : 1
     334        return 1;
     335    }
     336
     337    void merge(const MediaConstraint& other) final {
     338        if (other.isEmpty())
     339            return;
     340
     341        bool value;
     342        if (other.getExact(value))
     343            m_exact = value;
     344
     345        if (other.getIdeal(value)) {
     346            if (!m_ideal || (value && !m_ideal.value()))
     347                m_ideal = value;
     348        }
     349    }
     350
    218351private:
    219352    explicit BooleanConstraint(const String& name, MediaConstraintType type)
     
    229362public:
    230363    static Ref<StringConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new StringConstraint(name, type)); }
     364
     365    Ref<MediaConstraint> copy() const final;
    231366
    232367    void setExact(const String&);
     
    241376    bool isMandatory() const final { return !m_exact.isEmpty(); }
    242377
    243     const String& find(std::function<bool(ConstraintType, const String&)>) const override;
     378    double fitnessDistance(const String&) const final;
     379    double fitnessDistance(const Vector<String>&) const final;
     380
     381    const String& find(std::function<bool(ConstraintType, const String&)>) const final;
     382    void merge(const MediaConstraint&) final;
    244383
    245384private:
     
    268407
    269408using MediaTrackConstraintSetMap = HashMap<String, RefPtr<MediaConstraint>>;
     409
     410class FlattenedConstraint {
     411public:
     412    typedef Vector<RefPtr<MediaConstraint>>::const_iterator const_iterator;
     413
     414    void set(const MediaConstraint&);
     415    void merge(const MediaConstraint&);
     416    bool isEmpty() const { return m_constraints.isEmpty(); }
     417
     418    const_iterator begin() const { return m_constraints.begin(); }
     419    const_iterator end() const { return m_constraints.end(); }
     420
     421private:
     422
     423    Vector<RefPtr<MediaConstraint>> m_constraints;
     424};
    270425
    271426class MediaConstraints : public RefCounted<MediaConstraints> {
  • trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp

    r205348 r205574  
    151151}
    152152
    153 RealtimeMediaSource::ConstraintSupport RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint)
     153double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
    154154{
    155155    RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
     
    158158    case MediaConstraintType::Width: {
    159159        if (!capabilities.supportsWidth())
    160             return ConstraintSupport::Ignored;
    161 
    162         auto widthRange = capabilities.width();
    163         return constraint.validForRange(widthRange.rangeMin().asInt, widthRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     160            return 0;
     161
     162        auto range = capabilities.width();
     163        return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
    164164        break;
    165165    }
     
    167167    case MediaConstraintType::Height: {
    168168        if (!capabilities.supportsHeight())
    169             return ConstraintSupport::Ignored;
    170 
    171         auto heightRange = capabilities.height();
    172         return constraint.validForRange(heightRange.rangeMin().asInt, heightRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     169            return 0;
     170
     171        auto range = capabilities.height();
     172        return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
    173173        break;
    174174    }
     
    176176    case MediaConstraintType::FrameRate: {
    177177        if (!capabilities.supportsFrameRate())
    178             return ConstraintSupport::Ignored;
    179 
    180         auto rateRange = capabilities.frameRate();
    181         return constraint.validForRange(rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     178            return 0;
     179
     180        auto range = capabilities.frameRate();
     181        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
    182182        break;
    183183    }
     
    185185    case MediaConstraintType::AspectRatio: {
    186186        if (!capabilities.supportsAspectRatio())
    187             return ConstraintSupport::Ignored;
     187            return 0;
    188188
    189189        auto range = capabilities.aspectRatio();
    190         return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     190        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
    191191        break;
    192192    }
     
    194194    case MediaConstraintType::Volume: {
    195195        if (!capabilities.supportsVolume())
    196             return ConstraintSupport::Ignored;
     196            return 0;
    197197
    198198        auto range = capabilities.volume();
    199         return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     199        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
    200200        break;
    201201    }
     
    203203    case MediaConstraintType::SampleRate: {
    204204        if (!capabilities.supportsSampleRate())
    205             return ConstraintSupport::Ignored;
     205            return 0;
    206206
    207207        auto range = capabilities.sampleRate();
    208         return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     208        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
    209209        break;
    210210    }
     
    212212    case MediaConstraintType::SampleSize: {
    213213        if (!capabilities.supportsSampleSize())
    214             return ConstraintSupport::Ignored;
     214            return 0;
    215215
    216216        auto range = capabilities.sampleSize();
    217         return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
     217        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
    218218        break;
    219219    }
     
    221221    case MediaConstraintType::FacingMode: {
    222222        if (!capabilities.supportsFacingMode())
    223             return ConstraintSupport::Ignored;
    224 
    225         ConstraintSupport support = ConstraintSupport::Ignored;
    226         auto& supportedModes = capabilities.facingMode();
    227         std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes, &support](MediaConstraint::ConstraintType type, const String& modeString) {
    228             if (type == MediaConstraint::ConstraintType::ExactConstraint)
    229                 support = ConstraintSupport::Unsupported;
    230 
    231             auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
    232             for (auto& supportedMode : supportedModes) {
    233                 if (supportedMode == mode) {
    234                     support = ConstraintSupport::Supported;
    235                     break;
    236                 }
    237             }
    238 
    239             return type == MediaConstraint::ConstraintType::ExactConstraint ? true : false;
    240         };
    241 
    242         constraint.find(filter);
    243         return support;
    244         break;
    245     }
    246 
    247     case MediaConstraintType::EchoCancellation:
     223            return 0;
     224
     225        auto& modes = capabilities.facingMode();
     226        Vector<String> supportedModes;
     227        supportedModes.reserveInitialCapacity(modes.size());
     228        for (auto& mode : modes)
     229            supportedModes.append(RealtimeMediaSourceSettings::facingMode(mode));
     230        return constraint.fitnessDistance(supportedModes);
     231        break;
     232    }
     233
     234    case MediaConstraintType::EchoCancellation: {
    248235        if (!capabilities.supportsEchoCancellation())
    249             return ConstraintSupport::Ignored;
    250 
    251         if (capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadOnly)
    252             return constraint.isMandatory() ? ConstraintSupport::Unsupported : ConstraintSupport::Ignored;
    253 
    254         return ConstraintSupport::Supported;
    255         break;
     236            return 0;
     237
     238        bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
     239        return constraint.fitnessDistance(echoCancellationReadWrite);
     240        break;
     241    }
    256242
    257243    case MediaConstraintType::DeviceId: {
    258244        if (!capabilities.supportsDeviceId())
    259             return ConstraintSupport::Ignored;
    260 
    261         ConstraintSupport support = ConstraintSupport::Ignored;
    262         std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [this, &support](MediaConstraint::ConstraintType type, const String& idString) {
    263             if (type != MediaConstraint::ConstraintType::ExactConstraint)
    264                 return false; // Keep looking.
    265 
    266             support = idString == m_id ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
    267             return false;
    268         };
    269 
    270         constraint.find(filter);
    271         return support;
     245            return 0;
     246
     247        return constraint.fitnessDistance(m_id);
    272248        break;
    273249    }
     
    275251    case MediaConstraintType::GroupId: {
    276252        if (!capabilities.supportsDeviceId())
    277             return ConstraintSupport::Ignored;
    278 
    279         ConstraintSupport support = ConstraintSupport::Ignored;
    280         String groupId = settings().groupId();
    281         std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [groupId, &support](MediaConstraint::ConstraintType type, const String& idString) {
    282             if (type != MediaConstraint::ConstraintType::ExactConstraint)
    283                 return false; // Keep looking.
    284 
    285             support = idString == groupId ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
    286             return false;
    287         };
    288 
    289         constraint.find(filter);
    290         return support;
     253            return 0;
     254
     255        return constraint.fitnessDistance(settings().groupId());
    291256        break;
    292257    }
     
    297262    }
    298263
    299     return ConstraintSupport::Ignored;
    300 }
    301 
    302 
    303 template <typename T>
    304 T value(const MediaConstraint& constraint, T rangeMin, T rangeMax)
    305 {
    306     T result;
    307 
    308     if (constraint.getExact(result)) {
    309         ASSERT(result >= rangeMin && result <= rangeMax);
    310         return result;
    311     }
    312 
    313     if (constraint.getIdeal(result)) {
    314         if (result < rangeMin)
    315             result = rangeMin;
    316         else if (result > rangeMax)
    317             result = rangeMax;
    318 
    319         return result;
    320     }
    321 
    322     if (constraint.getMin(result) && result > rangeMax)
    323         return false;
    324 
    325     if (constraint.getMax(result) && result < rangeMin)
    326         return false;
    327    
    328     return result;
    329 }
    330 
     264    return 0;
     265}
     266
     267template <typename ValueType>
     268static void applyNumericConstraint(const MediaConstraint& constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*function)(ValueType))
     269{
     270    ValueType value;
     271    ValueType min = capabilityMin;
     272    ValueType max = capabilityMax;
     273
     274    if (constraint.getExact(value)) {
     275        ASSERT(constraint.validForRange(capabilityMin, capabilityMax));
     276        (source->*function)(value);
     277        return;
     278    }
     279
     280    if (constraint.getMin(value)) {
     281        ASSERT(constraint.validForRange(value, capabilityMax));
     282        if (value > min)
     283            min = value;
     284
     285        // If there is no ideal, don't change if minimum is smaller than current.
     286        ValueType ideal;
     287        if (!constraint.getIdeal(ideal) && value < current)
     288            value = current;
     289    }
     290
     291    if (constraint.getMax(value)) {
     292        ASSERT(constraint.validForRange(capabilityMin, value));
     293        if (value < max)
     294            max = value;
     295    }
     296
     297    if (constraint.getIdeal(value)) {
     298        if (value < min)
     299            value = min;
     300        if (value > max)
     301            value = max;
     302    }
     303
     304    if (value != current)
     305        (source->*function)(value);
     306}
    331307
    332308void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
     
    338314            return;
    339315
    340         auto widthRange = capabilities.width();
    341         setWidth(value(constraint, widthRange.rangeMin().asInt, widthRange.rangeMax().asInt));
     316        auto range = capabilities.width();
     317        applyNumericConstraint(constraint, size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setWidth);
    342318        break;
    343319    }
     
    347323            return;
    348324
    349         auto heightRange = capabilities.height();
    350         setHeight(value(constraint, heightRange.rangeMin().asInt, heightRange.rangeMax().asInt));
     325        auto range = capabilities.height();
     326        applyNumericConstraint(constraint, size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setHeight);
    351327        break;
    352328    }
     
    356332            return;
    357333
    358         auto rateRange = capabilities.frameRate();
    359         setFrameRate(value(constraint, rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble));
     334        auto range = capabilities.frameRate();
     335        applyNumericConstraint(constraint, frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setFrameRate);
    360336        break;
    361337    }
     
    366342
    367343        auto range = capabilities.aspectRatio();
    368         setAspectRatio(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
     344        applyNumericConstraint(constraint, aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setAspectRatio);
    369345        break;
    370346    }
     
    375351
    376352        auto range = capabilities.volume();
    377         // std::pair<T, T> valuesForRange(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)
    378         setVolume(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
     353        applyNumericConstraint(constraint, volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setVolume);
    379354        break;
    380355    }
     
    385360
    386361        auto range = capabilities.sampleRate();
    387         setSampleRate(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
     362        applyNumericConstraint(constraint, sampleRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setSampleRate);
    388363        break;
    389364    }
     
    394369
    395370        auto range = capabilities.sampleSize();
    396         setSampleSize(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
     371        applyNumericConstraint(constraint, sampleSize(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setSampleSize);
    397372        break;
    398373    }
     
    437412}
    438413
    439 void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
    440 {
    441     ASSERT(constraints.isValid());
    442 
     414bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
     415{
     416    // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
     417    //
     418    // 1. Each constraint specifies one or more values (or a range of values) for its property.
     419    //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
     420    //    empty object or list has been given as the value for a constraint, it must be interpreted
     421    //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
     422    //
     423    //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
     424    //    constraints will silently disappear. To avoid this being a surprise, application authors are
     425    //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
     426
     427    // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
     428    //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
     429    //    removed.)
     430
     431    // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
     432    //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
     433    //    distance is finite.
    443434    auto& mandatoryConstraints = constraints.mandatoryConstraints();
    444435    for (auto& nameConstraintPair : mandatoryConstraints) {
    445         auto& constraint = *nameConstraintPair.value;
    446         if (supportsConstraint(constraint) == ConstraintSupport::Unsupported) {
    447             failureHandler(constraint.name(), "Constraint not supported");
    448             return;
     436        const auto& constraint = nameConstraintPair.value;
     437        if (fitnessDistance(*constraint) == std::numeric_limits<double>::infinity()) {
     438            failedConstraint = constraint->name();
     439            return false;
    449440        }
    450     }
    451 
    452     for (auto& nameConstraintPair : mandatoryConstraints)
    453         applyConstraint(*nameConstraintPair.value);
     441
     442        candidates.set(*constraint);
     443    }
     444
     445    // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
     446    if (candidates.isEmpty())
     447        return true;
     448
     449    // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
     450    //    For each ConstraintSet:
     451
     452    // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
     453    //     values of properties as exact.
     454    Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
     455    double minimumDistance = std::numeric_limits<double>::infinity();
     456    for (const auto& advancedConstraint : constraints.advancedConstraints()) {
     457        double constraintDistance = 0;
     458        bool supported = false;
     459        for (auto& nameConstraintPair : advancedConstraint) {
     460            double distance = fitnessDistance(*nameConstraintPair.value);
     461            constraintDistance += distance;
     462            if (distance != std::numeric_limits<double>::infinity())
     463                supported = true;
     464        }
     465
     466        if (constraintDistance < minimumDistance)
     467            minimumDistance = constraintDistance;
     468
     469        // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
     470        //     settings dictionaries in candidates, discarding others.
     471        //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
     472        if (supported)
     473            supportedConstraints.append({constraintDistance, advancedConstraint});
     474    }
     475
     476    // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
     477    //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
     478    if (minimumDistance != std::numeric_limits<double>::infinity()) {
     479        supportedConstraints.removeAllMatching( [&] (std::pair<double, MediaTrackConstraintSetMap> pair) -> bool {
     480            return pair.first > minimumDistance;
     481        });
     482
     483        if (!supportedConstraints.isEmpty()) {
     484            auto& advancedConstraint = supportedConstraints[0].second;
     485            for (auto& nameConstraintPair : advancedConstraint)
     486                candidates.merge(*nameConstraintPair.value);
     487        }
     488    }
     489
     490    return true;
     491}
     492
     493
     494void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
     495{
     496    ASSERT(constraints.isValid());
     497
     498    FlattenedConstraint candidates;
     499
     500    String failedConstraint;
     501    if (!selectSettings(constraints, candidates, failedConstraint)) {
     502        failureHandler(failedConstraint, "Constraint not supported");
     503        return;
     504    }
     505
     506    for (auto constraint : candidates)
     507        applyConstraint(*constraint);
    454508
    455509    successHandler();
  • trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h

    r205348 r205574  
    4343#include "PlatformLayer.h"
    4444#include "RealtimeMediaSourceCapabilities.h"
    45 #include <wtf/Lock.h>
    4645#include <wtf/RefCounted.h>
    4746#include <wtf/Vector.h>
     
    172171    WeakPtr<RealtimeMediaSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
    173172
    174     enum ConstraintSupport { Ignored, Supported, Unsupported };
    175     ConstraintSupport supportsConstraint(const MediaConstraint&);
     173    bool supportsConstraints(const MediaTrackConstraintSetMap&);
     174    bool selectSettings(const MediaConstraints&, FlattenedConstraint&, String&);
     175    double fitnessDistance(const MediaConstraint&);
    176176    void applyConstraint(const MediaConstraint&);
    177177
    178178    WeakPtrFactory<RealtimeMediaSource> m_weakPtrFactory;
    179     Lock m_lock;
    180 
    181179    String m_id;
    182180    String m_persistentID;
Note: See TracChangeset for help on using the changeset viewer.