2e9d965a199b5b321ad3fe30e18188f22e2b41df
[WebKit-https.git] / Source / WebCore / platform / mediastream / RealtimeMediaSource.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
4  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
5  * Copyright (C) 2015 Ericsson AB. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer
15  *    in the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of Google Inc. nor the names of its contributors
18  *    may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "config.h"
35
36 #if ENABLE(MEDIA_STREAM)
37 #include "RealtimeMediaSource.h"
38
39 #include "Logging.h"
40 #include "MediaConstraints.h"
41 #include "NotImplemented.h"
42 #include "RealtimeMediaSourceCapabilities.h"
43 #include "RealtimeMediaSourceCenter.h"
44 #include <wtf/MainThread.h>
45 #include <wtf/UUID.h>
46 #include <wtf/text/StringHash.h>
47
48 namespace WebCore {
49
50 RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const String& name)
51     : m_id(id)
52     , m_type(type)
53     , m_name(name)
54 {
55     // FIXME(147205): Need to implement fitness score for constraints
56
57     if (m_id.isEmpty())
58         m_id = createCanonicalUUIDString();
59     m_persistentID = m_id;
60 }
61
62 void RealtimeMediaSource::addObserver(RealtimeMediaSource::Observer& observer)
63 {
64     auto locker = holdLock(m_observersLock);
65     m_observers.add(&observer);
66 }
67
68 void RealtimeMediaSource::removeObserver(RealtimeMediaSource::Observer& observer)
69 {
70     auto locker = holdLock(m_observersLock);
71
72     m_observers.remove(&observer);
73     if (m_observers.isEmpty())
74         stop();
75 }
76
77 void RealtimeMediaSource::setInterrupted(bool interrupted, bool pageMuted)
78 {
79     if (interrupted == m_interrupted)
80         return;
81
82     m_interrupted = interrupted;
83     if (!interrupted && pageMuted)
84         return;
85
86     setMuted(interrupted);
87 }
88
89 void RealtimeMediaSource::setMuted(bool muted)
90 {
91     if (muted)
92         stop();
93     else {
94         if (interrupted())
95             return;
96
97         start();
98     }
99
100     notifyMutedChange(muted);
101 }
102
103 void RealtimeMediaSource::notifyMutedChange(bool muted)
104 {
105     if (m_muted == muted)
106         return;
107
108     m_muted = muted;
109
110     notifyMutedObservers();
111 }
112
113 void RealtimeMediaSource::forEachObserver(const WTF::Function<void(Observer&)>& apply) const
114 {
115     Vector<Observer*> observersCopy;
116     {
117         auto locker = holdLock(m_observersLock);
118         observersCopy = copyToVector(m_observers);
119     }
120     for (auto* observer : observersCopy) {
121         auto locker = holdLock(m_observersLock);
122         // Make sure the observer has not been destroyed.
123         if (!m_observers.contains(observer))
124             continue;
125         apply(*observer);
126     }
127 }
128
129 void RealtimeMediaSource::notifyMutedObservers() const
130 {
131     forEachObserver([](auto& observer) {
132         observer.sourceMutedChanged();
133     });
134 }
135
136 void RealtimeMediaSource::settingsDidChange()
137 {
138     ASSERT(isMainThread());
139
140     if (m_pendingSettingsDidChangeNotification)
141         return;
142
143     m_pendingSettingsDidChangeNotification = true;
144
145     scheduleDeferredTask([this] {
146         m_pendingSettingsDidChangeNotification = false;
147         forEachObserver([](auto& observer) {
148             observer.sourceSettingsChanged();
149         });
150     });
151 }
152
153 void RealtimeMediaSource::videoSampleAvailable(MediaSample& mediaSample)
154 {
155     forEachObserver([&](auto& observer) {
156         observer.videoSampleAvailable(mediaSample);
157     });
158 }
159
160 void RealtimeMediaSource::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
161 {
162     forEachObserver([&](auto& observer) {
163         observer.audioSamplesAvailable(time, audioData, description, numberOfFrames);
164     });
165 }
166
167 void RealtimeMediaSource::start()
168 {
169     if (m_isProducingData)
170         return;
171
172     m_isProducingData = true;
173     startProducingData();
174
175     if (!m_isProducingData)
176         return;
177
178     forEachObserver([](auto& observer) {
179         observer.sourceStarted();
180     });
181 }
182
183 void RealtimeMediaSource::stop()
184 {
185     if (!m_isProducingData)
186         return;
187
188     m_isProducingData = false;
189     stopProducingData();
190 }
191
192 void RealtimeMediaSource::requestStop(Observer* callingObserver)
193 {
194     if (!m_isProducingData)
195         return;
196
197     bool hasObserverPreventingStopping = false;
198     forEachObserver([&](auto& observer) {
199         if (observer.preventSourceFromStopping())
200             hasObserverPreventingStopping = true;
201     });
202     if (hasObserverPreventingStopping)
203         return;
204
205     stop();
206
207     forEachObserver([callingObserver](auto& observer) {
208         if (&observer != callingObserver)
209             observer.sourceStopped();
210     });
211 }
212
213 void RealtimeMediaSource::captureFailed()
214 {
215     RELEASE_LOG_ERROR(MediaStream, "RealtimeMediaSource::captureFailed");
216
217     m_isProducingData = false;
218     m_captureDidFailed = true;
219
220     forEachObserver([](auto& observer) {
221         observer.sourceStopped();
222     });
223 }
224
225 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<int>, std::optional<int>, std::optional<double>)
226 {
227     // The size and frame rate are within the capability limits, so they are supported.
228     return true;
229 }
230
231 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<IntConstraint> widthConstraint, std::optional<IntConstraint> heightConstraint, std::optional<DoubleConstraint> frameRateConstraint, String& badConstraint, double& distance)
232 {
233     if (!widthConstraint && !heightConstraint && !frameRateConstraint)
234         return true;
235
236     auto& capabilities = this->capabilities();
237
238     distance = std::numeric_limits<double>::infinity();
239
240     std::optional<int> width;
241     if (widthConstraint && capabilities.supportsWidth()) {
242         double constraintDistance = fitnessDistance(*widthConstraint);
243         if (std::isinf(constraintDistance)) {
244             badConstraint = widthConstraint->name();
245             return false;
246         }
247
248         distance = std::min(distance, constraintDistance);
249         if (widthConstraint->isMandatory()) {
250             auto range = capabilities.width();
251             width = widthConstraint->valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
252         }
253     }
254
255     std::optional<int> height;
256     if (heightConstraint && capabilities.supportsHeight()) {
257         double constraintDistance = fitnessDistance(*heightConstraint);
258         if (std::isinf(constraintDistance)) {
259             badConstraint = heightConstraint->name();
260             return false;
261         }
262
263         distance = std::min(distance, constraintDistance);
264         if (heightConstraint->isMandatory()) {
265             auto range = capabilities.height();
266             height = heightConstraint->valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
267         }
268     }
269
270     std::optional<double> frameRate;
271     if (frameRateConstraint && capabilities.supportsFrameRate()) {
272         double constraintDistance = fitnessDistance(*frameRateConstraint);
273         if (std::isinf(constraintDistance)) {
274             badConstraint = frameRateConstraint->name();
275             return false;
276         }
277
278         distance = std::min(distance, constraintDistance);
279         if (frameRateConstraint->isMandatory()) {
280             auto range = capabilities.frameRate();
281             frameRate = frameRateConstraint->valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
282         }
283     }
284
285     // Each of the non-null values is supported individually, see if they all can be applied at the same time.
286     if (!supportsSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate))) {
287         if (widthConstraint)
288             badConstraint = widthConstraint->name();
289         else if (heightConstraint)
290             badConstraint = heightConstraint->name();
291         else
292             badConstraint = frameRateConstraint->name();
293         return false;
294     }
295
296     return true;
297 }
298
299 double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
300 {
301     auto& capabilities = this->capabilities();
302
303     switch (constraint.constraintType()) {
304     case MediaConstraintType::Width: {
305         ASSERT(constraint.isInt());
306         if (!capabilities.supportsWidth())
307             return 0;
308
309         auto range = capabilities.width();
310         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
311         break;
312     }
313
314     case MediaConstraintType::Height: {
315         ASSERT(constraint.isInt());
316         if (!capabilities.supportsHeight())
317             return 0;
318
319         auto range = capabilities.height();
320         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
321         break;
322     }
323
324     case MediaConstraintType::FrameRate: {
325         ASSERT(constraint.isDouble());
326         if (!capabilities.supportsFrameRate())
327             return 0;
328
329         auto range = capabilities.frameRate();
330         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
331         break;
332     }
333
334     case MediaConstraintType::AspectRatio: {
335         ASSERT(constraint.isDouble());
336         if (!capabilities.supportsAspectRatio())
337             return 0;
338
339         auto range = capabilities.aspectRatio();
340         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
341         break;
342     }
343
344     case MediaConstraintType::Volume: {
345         ASSERT(constraint.isDouble());
346         if (!capabilities.supportsVolume())
347             return 0;
348
349         auto range = capabilities.volume();
350         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
351         break;
352     }
353
354     case MediaConstraintType::SampleRate: {
355         ASSERT(constraint.isInt());
356         if (!capabilities.supportsSampleRate())
357             return 0;
358
359         auto range = capabilities.sampleRate();
360         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
361         break;
362     }
363
364     case MediaConstraintType::SampleSize: {
365         ASSERT(constraint.isInt());
366         if (!capabilities.supportsSampleSize())
367             return 0;
368
369         auto range = capabilities.sampleSize();
370         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
371         break;
372     }
373
374     case MediaConstraintType::FacingMode: {
375         ASSERT(constraint.isString());
376         if (!capabilities.supportsFacingMode())
377             return 0;
378
379         auto& modes = capabilities.facingMode();
380         Vector<String> supportedModes;
381         supportedModes.reserveInitialCapacity(modes.size());
382         for (auto& mode : modes)
383             supportedModes.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
384         return downcast<StringConstraint>(constraint).fitnessDistance(supportedModes);
385         break;
386     }
387
388     case MediaConstraintType::EchoCancellation: {
389         ASSERT(constraint.isBoolean());
390         if (!capabilities.supportsEchoCancellation())
391             return 0;
392
393         bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
394         return downcast<BooleanConstraint>(constraint).fitnessDistance(echoCancellationReadWrite);
395         break;
396     }
397
398     case MediaConstraintType::DeviceId:
399         ASSERT_NOT_REACHED();
400         break;
401
402     case MediaConstraintType::GroupId: {
403         ASSERT(constraint.isString());
404         if (!capabilities.supportsDeviceId())
405             return 0;
406
407         return downcast<StringConstraint>(constraint).fitnessDistance(settings().groupId());
408         break;
409     }
410
411     case MediaConstraintType::DisplaySurface:
412     case MediaConstraintType::LogicalSurface:
413         break;
414
415     case MediaConstraintType::Unknown:
416         // Unknown (or unsupported) constraints should be ignored.
417         break;
418     }
419
420     return 0;
421 }
422
423 template <typename ValueType>
424 static void applyNumericConstraint(const NumericConstraint<ValueType>& constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*applier)(ValueType))
425 {
426     ValueType value = constraint.valueForCapabilityRange(current, capabilityMin, capabilityMax);
427     if (value != current)
428         (source->*applier)(value);
429 }
430
431 void RealtimeMediaSource::applySizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
432 {
433     if (width)
434         setWidth(width.value());
435     if (height)
436         setHeight(height.value());
437     if (frameRate)
438         setFrameRate(frameRate.value());
439 }
440
441 void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
442 {
443     auto& capabilities = this->capabilities();
444     switch (constraint.constraintType()) {
445     case MediaConstraintType::Width: {
446         ASSERT(constraint.isInt());
447         if (!capabilities.supportsWidth())
448             return;
449
450         auto range = capabilities.width();
451         applyNumericConstraint(downcast<IntConstraint>(constraint), size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setWidth);
452         break;
453     }
454
455     case MediaConstraintType::Height: {
456         ASSERT(constraint.isInt());
457         if (!capabilities.supportsHeight())
458             return;
459
460         auto range = capabilities.height();
461         applyNumericConstraint(downcast<IntConstraint>(constraint), size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setHeight);
462         break;
463     }
464
465     case MediaConstraintType::FrameRate: {
466         ASSERT(constraint.isDouble());
467         if (!capabilities.supportsFrameRate())
468             return;
469
470         auto range = capabilities.frameRate();
471         applyNumericConstraint(downcast<DoubleConstraint>(constraint), frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setFrameRate);
472         break;
473     }
474
475     case MediaConstraintType::AspectRatio: {
476         ASSERT(constraint.isDouble());
477         if (!capabilities.supportsAspectRatio())
478             return;
479
480         auto range = capabilities.aspectRatio();
481         applyNumericConstraint(downcast<DoubleConstraint>(constraint), aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setAspectRatio);
482         break;
483     }
484
485     case MediaConstraintType::Volume: {
486         ASSERT(constraint.isDouble());
487         if (!capabilities.supportsVolume())
488             return;
489
490         auto range = capabilities.volume();
491         applyNumericConstraint(downcast<DoubleConstraint>(constraint), volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setVolume);
492         break;
493     }
494
495     case MediaConstraintType::SampleRate: {
496         ASSERT(constraint.isInt());
497         if (!capabilities.supportsSampleRate())
498             return;
499
500         auto range = capabilities.sampleRate();
501         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleRate(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleRate);
502         break;
503     }
504
505     case MediaConstraintType::SampleSize: {
506         ASSERT(constraint.isInt());
507         if (!capabilities.supportsSampleSize())
508             return;
509
510         auto range = capabilities.sampleSize();
511         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleSize(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleSize);
512         break;
513     }
514
515     case MediaConstraintType::EchoCancellation: {
516         ASSERT(constraint.isBoolean());
517         if (!capabilities.supportsEchoCancellation())
518             return;
519
520         bool setting;
521         const BooleanConstraint& boolConstraint = downcast<BooleanConstraint>(constraint);
522         if (boolConstraint.getExact(setting) || boolConstraint.getIdeal(setting))
523             setEchoCancellation(setting);
524         break;
525     }
526
527     case MediaConstraintType::FacingMode: {
528         ASSERT(constraint.isString());
529         if (!capabilities.supportsFacingMode())
530             return;
531
532         auto& supportedModes = capabilities.facingMode();
533         auto filter = [supportedModes](const String& modeString) {
534             auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
535             for (auto& supportedMode : supportedModes) {
536                 if (mode == supportedMode)
537                     return true;
538             }
539             return false;
540         };
541
542         auto modeString = downcast<StringConstraint>(constraint).find(WTFMove(filter));
543         if (!modeString.isEmpty())
544             setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString));
545         break;
546     }
547
548     case MediaConstraintType::DeviceId:
549     case MediaConstraintType::GroupId:
550         ASSERT(constraint.isString());
551         // There is nothing to do here, neither can be changed.
552         break;
553
554     case MediaConstraintType::DisplaySurface:
555     case MediaConstraintType::LogicalSurface:
556         ASSERT(constraint.isBoolean());
557         break;
558
559     case MediaConstraintType::Unknown:
560         break;
561     }
562 }
563
564 bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint, SelectType type)
565 {
566     m_fitnessScore = std::numeric_limits<double>::infinity();
567
568     // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
569     //
570     // 1. Each constraint specifies one or more values (or a range of values) for its property.
571     //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
572     //    empty object or list has been given as the value for a constraint, it must be interpreted
573     //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
574     //
575     //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
576     //    constraints will silently disappear. To avoid this being a surprise, application authors are
577     //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
578
579     // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
580     //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
581     //    removed.)
582
583     // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
584     //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
585     //    distance is finite.
586
587     failedConstraint = emptyString();
588
589     // Check width, height and frame rate jointly, because while they may be supported individually the combination may not be supported.
590     double distance = std::numeric_limits<double>::infinity();
591     if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints.width(), constraints.mandatoryConstraints.height(), constraints.mandatoryConstraints.frameRate(), failedConstraint, m_fitnessScore))
592         return false;
593
594     constraints.mandatoryConstraints.filter([&](const MediaConstraint& constraint) {
595         if (!supportsConstraint(constraint))
596             return false;
597
598         if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate) {
599             candidates.set(constraint);
600             return false;
601         }
602
603         // The deviceId can't be changed, and the constraint value is the hashed device ID, so verify that the
604         // device's unique ID hashes to the constraint value but don't include the constraint in the flattened
605         // constraint set.
606         if (constraint.constraintType() == MediaConstraintType::DeviceId) {
607             if (type == SelectType::ForApplyConstraints)
608                 return false;
609
610             ASSERT(constraint.isString());
611             ASSERT(!constraints.deviceIDHashSalt.isEmpty());
612
613             auto hashedID = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(m_persistentID, constraints.deviceIDHashSalt);
614             double constraintDistance = downcast<StringConstraint>(constraint).fitnessDistance(hashedID);
615             if (std::isinf(constraintDistance)) {
616                 failedConstraint = constraint.name();
617                 return true;
618             }
619
620             return false;
621         }
622
623         double constraintDistance = fitnessDistance(constraint);
624         if (std::isinf(constraintDistance)) {
625             failedConstraint = constraint.name();
626             return true;
627         }
628
629         distance = std::min(distance, constraintDistance);
630         candidates.set(constraint);
631         return false;
632     });
633
634     if (!failedConstraint.isEmpty())
635         return false;
636
637     m_fitnessScore = distance;
638
639     // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
640     if (candidates.isEmpty())
641         return true;
642
643     // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
644     //    For each ConstraintSet:
645
646     // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
647     //     values of properties as exact.
648     Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
649
650     for (const auto& advancedConstraint : constraints.advancedConstraints) {
651         double constraintDistance = 0;
652         bool supported = false;
653
654         if (advancedConstraint.width() || advancedConstraint.height() || advancedConstraint.frameRate()) {
655             String dummy;
656             if (!supportsSizeAndFrameRate(advancedConstraint.width(), advancedConstraint.height(), advancedConstraint.frameRate(), dummy, constraintDistance))
657                 continue;
658
659             supported = true;
660         }
661
662         advancedConstraint.forEach([&](const MediaConstraint& constraint) {
663
664             if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate)
665                 return;
666
667             distance = fitnessDistance(constraint);
668             constraintDistance += distance;
669             if (!std::isinf(distance))
670                 supported = true;
671         });
672
673         m_fitnessScore = std::min(m_fitnessScore, constraintDistance);
674
675         // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
676         //     settings dictionaries in candidates, discarding others.
677         //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
678         if (supported)
679             supportedConstraints.append({constraintDistance, advancedConstraint});
680     }
681
682     // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
683     //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
684     if (!supportedConstraints.isEmpty()) {
685         supportedConstraints.removeAllMatching([&](const std::pair<double, MediaTrackConstraintSetMap>& pair) -> bool {
686             return std::isinf(pair.first) || pair.first > m_fitnessScore;
687         });
688
689         if (!supportedConstraints.isEmpty()) {
690             auto& advancedConstraint = supportedConstraints[0].second;
691             advancedConstraint.forEach([&](const MediaConstraint& constraint) {
692                 candidates.merge(constraint);
693             });
694
695             m_fitnessScore = std::min(m_fitnessScore, supportedConstraints[0].first);
696         }
697     }
698
699     return true;
700 }
701
702 bool RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint) const
703 {
704     auto& capabilities = this->capabilities();
705
706     switch (constraint.constraintType()) {
707     case MediaConstraintType::Width:
708         ASSERT(constraint.isInt());
709         return capabilities.supportsWidth();
710         break;
711
712     case MediaConstraintType::Height:
713         ASSERT(constraint.isInt());
714         return capabilities.supportsHeight();
715         break;
716
717     case MediaConstraintType::FrameRate:
718         ASSERT(constraint.isDouble());
719         return capabilities.supportsFrameRate();
720         break;
721
722     case MediaConstraintType::AspectRatio:
723         ASSERT(constraint.isDouble());
724         return capabilities.supportsAspectRatio();
725         break;
726
727     case MediaConstraintType::Volume:
728         ASSERT(constraint.isDouble());
729         return capabilities.supportsVolume();
730         break;
731
732     case MediaConstraintType::SampleRate:
733         ASSERT(constraint.isInt());
734         return capabilities.supportsSampleRate();
735         break;
736
737     case MediaConstraintType::SampleSize:
738         ASSERT(constraint.isInt());
739         return capabilities.supportsSampleSize();
740         break;
741
742     case MediaConstraintType::FacingMode:
743         ASSERT(constraint.isString());
744         return capabilities.supportsFacingMode();
745         break;
746
747     case MediaConstraintType::EchoCancellation:
748         ASSERT(constraint.isBoolean());
749         return capabilities.supportsEchoCancellation();
750         break;
751
752     case MediaConstraintType::DeviceId:
753         ASSERT(constraint.isString());
754         return capabilities.supportsDeviceId();
755         break;
756
757     case MediaConstraintType::GroupId:
758         ASSERT(constraint.isString());
759         return capabilities.supportsDeviceId();
760         break;
761
762     case MediaConstraintType::DisplaySurface:
763     case MediaConstraintType::LogicalSurface:
764         // https://www.w3.org/TR/screen-capture/#new-constraints-for-captured-display-surfaces
765         // 5.2.1 New Constraints for Captured Display Surfaces
766         // Since the source of media cannot be changed after a MediaStreamTrack has been returned,
767         // these constraints cannot be changed by an application.
768         return false;
769         break;
770
771     case MediaConstraintType::Unknown:
772         // Unknown (or unsupported) constraints should be ignored.
773         break;
774     }
775     
776     return false;
777 }
778
779 bool RealtimeMediaSource::supportsConstraints(const MediaConstraints& constraints, String& invalidConstraint)
780 {
781     ASSERT(constraints.isValid);
782
783     FlattenedConstraint candidates;
784     if (!selectSettings(constraints, candidates, invalidConstraint, SelectType::ForSupportsConstraints))
785         return false;
786     
787     return true;
788 }
789
790 void RealtimeMediaSource::applyConstraints(const FlattenedConstraint& constraints)
791 {
792     if (constraints.isEmpty())
793         return;
794
795     beginConfiguration();
796
797     auto& capabilities = this->capabilities();
798
799     std::optional<int> width;
800     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Width)) {
801         ASSERT(constraint->isInt());
802         if (capabilities.supportsWidth()) {
803             auto range = capabilities.width();
804             width = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
805         }
806     }
807
808     std::optional<int> height;
809     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Height)) {
810         ASSERT(constraint->isInt());
811         if (capabilities.supportsHeight()) {
812             auto range = capabilities.height();
813             height = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
814         }
815     }
816
817     std::optional<double> frameRate;
818     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::FrameRate)) {
819         ASSERT(constraint->isDouble());
820         if (capabilities.supportsFrameRate()) {
821             auto range = capabilities.frameRate();
822             frameRate = downcast<DoubleConstraint>(*constraint).valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
823         }
824     }
825
826     // FIXME: applySizeAndFrameRate should take MediaConstraint* instead of std::optional<> so it can see if a constraint is an exact, min, max,
827     // or ideal, and choose the correct value for properties with non-discreet capabilities when necessary.
828     if (width || height || frameRate)
829         applySizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate));
830
831     for (auto& variant : constraints) {
832         if (variant.constraintType() == MediaConstraintType::Width || variant.constraintType() == MediaConstraintType::Height || variant.constraintType() == MediaConstraintType::FrameRate)
833             continue;
834
835         applyConstraint(variant);
836     }
837
838     commitConfiguration();
839 }
840
841 std::optional<std::pair<String, String>> RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints)
842 {
843     ASSERT(constraints.isValid);
844
845     FlattenedConstraint candidates;
846     String failedConstraint;
847     if (!selectSettings(constraints, candidates, failedConstraint, SelectType::ForApplyConstraints))
848         return { { failedConstraint, "Constraint not supported"_s } };
849
850     applyConstraints(candidates);
851     return std::nullopt;
852 }
853
854 void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler&& successHandler, FailureHandler&& failureHandler)
855 {
856     auto result = applyConstraints(constraints);
857     if (!result && successHandler)
858         successHandler();
859     else if (result && failureHandler)
860         failureHandler(result.value().first, result.value().second);
861 }
862
863 void RealtimeMediaSource::setSize(const IntSize& size)
864 {
865     if (size == m_size)
866         return;
867
868     if (!applySize(size))
869         return;
870
871     m_size = size;
872     settingsDidChange();
873 }
874
875 void RealtimeMediaSource::setWidth(int width)
876 {
877     if (width == m_size.width())
878         return;
879
880     int height = m_aspectRatio ? width / m_aspectRatio : m_size.height();
881     if (!applySize(IntSize(width, height)))
882         return;
883
884     m_size.setWidth(width);
885     if (m_aspectRatio)
886         m_size.setHeight(width / m_aspectRatio);
887
888     settingsDidChange();
889 }
890
891 void RealtimeMediaSource::setHeight(int height)
892 {
893     if (height == m_size.height())
894         return;
895
896     int width = m_aspectRatio ? height * m_aspectRatio : m_size.width();
897     if (!applySize(IntSize(width, height)))
898         return;
899
900     if (m_aspectRatio)
901         m_size.setWidth(width);
902     m_size.setHeight(height);
903
904     settingsDidChange();
905 }
906
907 void RealtimeMediaSource::setFrameRate(double rate)
908 {
909     if (m_frameRate == rate || !applyFrameRate(rate))
910         return;
911
912     m_frameRate = rate;
913     settingsDidChange();
914 }
915
916 void RealtimeMediaSource::setAspectRatio(double ratio)
917 {
918     if (m_aspectRatio == ratio || !applyAspectRatio(ratio))
919         return;
920
921     m_aspectRatio = ratio;
922     m_size.setHeight(m_size.width() / ratio);
923     settingsDidChange();
924 }
925
926 void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
927 {
928     if (m_facingMode == mode || !applyFacingMode(mode))
929         return;
930
931     m_facingMode = mode;
932     settingsDidChange();
933 }
934
935 void RealtimeMediaSource::setVolume(double volume)
936 {
937     if (m_volume == volume || !applyVolume(volume))
938         return;
939
940     m_volume = volume;
941     settingsDidChange();
942 }
943
944 void RealtimeMediaSource::setSampleRate(int rate)
945 {
946     if (m_sampleRate == rate || !applySampleRate(rate))
947         return;
948
949     m_sampleRate = rate;
950     settingsDidChange();
951 }
952
953 void RealtimeMediaSource::setSampleSize(int size)
954 {
955     if (m_sampleSize == size || !applySampleSize(size))
956         return;
957
958     m_sampleSize = size;
959     settingsDidChange();
960 }
961
962 void RealtimeMediaSource::setEchoCancellation(bool echoCancellation)
963 {
964     if (m_echoCancellation == echoCancellation || !applyEchoCancellation(echoCancellation))
965         return;
966
967     m_echoCancellation = echoCancellation;
968     settingsDidChange();
969 }
970
971 void RealtimeMediaSource::scheduleDeferredTask(WTF::Function<void()>&& function)
972 {
973     ASSERT(function);
974     callOnMainThread([weakThis = makeWeakPtr(*this), function = WTFMove(function)] {
975         if (!weakThis)
976             return;
977
978         function();
979     });
980 }
981
982 RealtimeMediaSource::Observer::~Observer()
983 {
984 }
985
986 RealtimeMediaSource::AudioCaptureFactory::~AudioCaptureFactory()
987 {
988 }
989
990 RealtimeMediaSource::VideoCaptureFactory::~VideoCaptureFactory()
991 {
992 }
993
994 } // namespace WebCore
995
996 #endif // ENABLE(MEDIA_STREAM)