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