411ba82921a0e24ec241f5deab236723ed357aa1
[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 "MediaConstraints.h"
40 #include "NotImplemented.h"
41 #include "RealtimeMediaSourceCapabilities.h"
42 #include "UUID.h"
43 #include <wtf/MainThread.h>
44 #include <wtf/text/StringHash.h>
45
46 namespace WebCore {
47
48 RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const String& name)
49     : m_weakPtrFactory(this)
50     , m_id(id)
51     , m_type(type)
52     , m_name(name)
53 {
54     // FIXME(147205): Need to implement fitness score for constraints
55
56     if (m_id.isEmpty())
57         m_id = createCanonicalUUIDString();
58     m_persistentID = m_id;
59     m_suppressNotifications = false;
60 }
61
62 void RealtimeMediaSource::reset()
63 {
64     m_stopped = false;
65     m_muted = false;
66 }
67
68 void RealtimeMediaSource::addObserver(RealtimeMediaSource::Observer& observer)
69 {
70     m_observers.append(&observer);
71 }
72
73 void RealtimeMediaSource::removeObserver(RealtimeMediaSource::Observer& observer)
74 {
75     m_observers.removeFirstMatching([&observer](auto* anObserver) {
76         return anObserver == &observer;
77     });
78
79     if (!m_observers.size())
80         stop();
81 }
82
83 void RealtimeMediaSource::setMuted(bool muted)
84 {
85     if (m_stopped || m_muted == muted)
86         return;
87
88     m_muted = muted;
89
90     if (muted) {
91         // FIXME: We need to figure out how to guarantee that at least one black video frame is
92         // emitted after being muted.
93         stopProducingData();
94     } else
95         startProducingData();
96
97     notifyMutedObservers();
98 }
99
100 void RealtimeMediaSource::notifyMutedObservers() const
101 {
102     for (auto& observer : m_observers)
103         observer->sourceMutedChanged();
104 }
105
106 void RealtimeMediaSource::setEnabled(bool enabled)
107 {
108     if (m_stopped || m_enabled == enabled)
109         return;
110
111     m_enabled = enabled;
112
113     if (m_stopped)
114         return;
115
116     for (auto& observer : m_observers)
117         observer->sourceEnabledChanged();
118 }
119
120 void RealtimeMediaSource::settingsDidChange()
121 {
122     ASSERT(isMainThread());
123
124     if (m_pendingSettingsDidChangeNotification || m_suppressNotifications)
125         return;
126
127     m_pendingSettingsDidChangeNotification = true;
128
129     scheduleDeferredTask([this] {
130         m_pendingSettingsDidChangeNotification = false;
131         for (auto& observer : m_observers)
132             observer->sourceSettingsChanged();
133     });
134 }
135
136 void RealtimeMediaSource::videoSampleAvailable(MediaSample& mediaSample)
137 {
138     ASSERT(isMainThread());
139     for (const auto& observer : m_observers)
140         observer->videoSampleAvailable(mediaSample);
141 }
142
143 void RealtimeMediaSource::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
144 {
145     for (const auto& observer : m_observers)
146         observer->audioSamplesAvailable(time, audioData, description, numberOfFrames);
147 }
148
149 void RealtimeMediaSource::stop(Observer* callingObserver)
150 {
151     if (stopped())
152         return;
153
154     m_stopped = true;
155
156     for (const auto& observer : m_observers) {
157         if (observer != callingObserver)
158             observer->sourceStopped();
159     }
160
161     stopProducingData();
162 }
163
164 void RealtimeMediaSource::requestStop(Observer* callingObserver)
165 {
166     if (stopped())
167         return;
168
169     for (const auto& observer : m_observers) {
170         if (observer->preventSourceFromStopping())
171             return;
172     }
173     stop(callingObserver);
174 }
175
176 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<int>, std::optional<int>, std::optional<double>)
177 {
178     // The size and frame rate are within the capability limits, so they are supported.
179     return true;
180 }
181
182 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<IntConstraint> widthConstraint, std::optional<IntConstraint> heightConstraint, std::optional<DoubleConstraint> frameRateConstraint, String& badConstraint, double& distance)
183 {
184     if (!widthConstraint && !heightConstraint && !frameRateConstraint)
185         return true;
186
187     ASSERT(this->capabilities());
188     RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
189
190     distance = std::numeric_limits<double>::infinity();
191
192     std::optional<int> width;
193     if (widthConstraint && capabilities.supportsWidth()) {
194         double constraintDistance = fitnessDistance(*widthConstraint);
195         if (std::isinf(constraintDistance)) {
196             badConstraint = widthConstraint->name();
197             return false;
198         }
199
200         distance = std::min(distance, constraintDistance);
201         auto range = capabilities.width();
202         width = widthConstraint->valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
203     }
204
205     std::optional<int> height;
206     if (heightConstraint && capabilities.supportsHeight()) {
207         double constraintDistance = fitnessDistance(*heightConstraint);
208         if (std::isinf(constraintDistance)) {
209             badConstraint = heightConstraint->name();
210             return false;
211         }
212
213         distance = std::min(distance, constraintDistance);
214         auto range = capabilities.height();
215         height = heightConstraint->valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
216     }
217
218     std::optional<double> frameRate;
219     if (frameRateConstraint && capabilities.supportsFrameRate()) {
220         double constraintDistance = fitnessDistance(*frameRateConstraint);
221         if (std::isinf(constraintDistance)) {
222             badConstraint = frameRateConstraint->name();
223             return false;
224         }
225
226         distance = std::min(distance, constraintDistance);
227         auto range = capabilities.frameRate();
228         frameRate = frameRateConstraint->valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
229     }
230
231     // Each of the non-null values is supported individually, see if they all can be applied at the same time.
232     if (!supportsSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate))) {
233         if (widthConstraint)
234             badConstraint = widthConstraint->name();
235         else if (heightConstraint)
236             badConstraint = heightConstraint->name();
237         else
238             badConstraint = frameRateConstraint->name();
239         return false;
240     }
241     
242     return true;
243 }
244
245 double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
246 {
247     ASSERT(this->capabilities());
248     RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
249
250     switch (constraint.constraintType()) {
251     case MediaConstraintType::Width: {
252         ASSERT(constraint.isInt());
253         if (!capabilities.supportsWidth())
254             return 0;
255
256         auto range = capabilities.width();
257         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
258         break;
259     }
260
261     case MediaConstraintType::Height: {
262         ASSERT(constraint.isInt());
263         if (!capabilities.supportsHeight())
264             return 0;
265
266         auto range = capabilities.height();
267         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
268         break;
269     }
270
271     case MediaConstraintType::FrameRate: {
272         ASSERT(constraint.isDouble());
273         if (!capabilities.supportsFrameRate())
274             return 0;
275
276         auto range = capabilities.frameRate();
277         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
278         break;
279     }
280
281     case MediaConstraintType::AspectRatio: {
282         ASSERT(constraint.isDouble());
283         if (!capabilities.supportsAspectRatio())
284             return 0;
285
286         auto range = capabilities.aspectRatio();
287         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
288         break;
289     }
290
291     case MediaConstraintType::Volume: {
292         ASSERT(constraint.isDouble());
293         if (!capabilities.supportsVolume())
294             return 0;
295
296         auto range = capabilities.volume();
297         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
298         break;
299     }
300
301     case MediaConstraintType::SampleRate: {
302         ASSERT(constraint.isInt());
303         if (!capabilities.supportsSampleRate())
304             return 0;
305
306         auto range = capabilities.sampleRate();
307         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
308         break;
309     }
310
311     case MediaConstraintType::SampleSize: {
312         ASSERT(constraint.isInt());
313         if (!capabilities.supportsSampleSize())
314             return 0;
315
316         auto range = capabilities.sampleSize();
317         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
318         break;
319     }
320
321     case MediaConstraintType::FacingMode: {
322         ASSERT(constraint.isString());
323         if (!capabilities.supportsFacingMode())
324             return 0;
325
326         auto& modes = capabilities.facingMode();
327         Vector<String> supportedModes;
328         supportedModes.reserveInitialCapacity(modes.size());
329         for (auto& mode : modes)
330             supportedModes.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
331         return downcast<StringConstraint>(constraint).fitnessDistance(supportedModes);
332         break;
333     }
334
335     case MediaConstraintType::EchoCancellation: {
336         ASSERT(constraint.isBoolean());
337         if (!capabilities.supportsEchoCancellation())
338             return 0;
339
340         bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
341         return downcast<BooleanConstraint>(constraint).fitnessDistance(echoCancellationReadWrite);
342         break;
343     }
344
345     case MediaConstraintType::DeviceId: {
346         ASSERT(constraint.isString());
347         if (!capabilities.supportsDeviceId())
348             return 0;
349
350         return downcast<StringConstraint>(constraint).fitnessDistance(m_id);
351         break;
352     }
353
354     case MediaConstraintType::GroupId: {
355         ASSERT(constraint.isString());
356         if (!capabilities.supportsDeviceId())
357             return 0;
358
359         return downcast<StringConstraint>(constraint).fitnessDistance(settings().groupId());
360         break;
361     }
362
363     case MediaConstraintType::Unknown:
364         // Unknown (or unsupported) constraints should be ignored.
365         break;
366     }
367
368     return 0;
369 }
370
371 template <typename ValueType>
372 static void applyNumericConstraint(const NumericConstraint<ValueType>& constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*applier)(ValueType))
373 {
374     ValueType value = constraint.valueForCapabilityRange(current, capabilityMin, capabilityMax);
375     if (value != current)
376         (source->*applier)(value);
377 }
378
379 void RealtimeMediaSource::applySizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
380 {
381     if (width)
382         setWidth(width.value());
383     if (height)
384         setHeight(height.value());
385     if (frameRate)
386         setFrameRate(frameRate.value());
387 }
388
389 void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
390 {
391     RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
392     switch (constraint.constraintType()) {
393     case MediaConstraintType::Width: {
394         ASSERT(constraint.isInt());
395         if (!capabilities.supportsWidth())
396             return;
397
398         auto range = capabilities.width();
399         applyNumericConstraint(downcast<IntConstraint>(constraint), size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setWidth);
400         break;
401     }
402
403     case MediaConstraintType::Height: {
404         ASSERT(constraint.isInt());
405         if (!capabilities.supportsHeight())
406             return;
407
408         auto range = capabilities.height();
409         applyNumericConstraint(downcast<IntConstraint>(constraint), size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setHeight);
410         break;
411     }
412
413     case MediaConstraintType::FrameRate: {
414         ASSERT(constraint.isDouble());
415         if (!capabilities.supportsFrameRate())
416             return;
417
418         auto range = capabilities.frameRate();
419         applyNumericConstraint(downcast<DoubleConstraint>(constraint), frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setFrameRate);
420         break;
421     }
422
423     case MediaConstraintType::AspectRatio: {
424         ASSERT(constraint.isDouble());
425         if (!capabilities.supportsAspectRatio())
426             return;
427
428         auto range = capabilities.aspectRatio();
429         applyNumericConstraint(downcast<DoubleConstraint>(constraint), aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setAspectRatio);
430         break;
431     }
432
433     case MediaConstraintType::Volume: {
434         ASSERT(constraint.isDouble());
435         if (!capabilities.supportsVolume())
436             return;
437
438         auto range = capabilities.volume();
439         applyNumericConstraint(downcast<DoubleConstraint>(constraint), volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setVolume);
440         break;
441     }
442
443     case MediaConstraintType::SampleRate: {
444         ASSERT(constraint.isInt());
445         if (!capabilities.supportsSampleRate())
446             return;
447
448         auto range = capabilities.sampleRate();
449         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleRate(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleRate);
450         break;
451     }
452
453     case MediaConstraintType::SampleSize: {
454         ASSERT(constraint.isInt());
455         if (!capabilities.supportsSampleSize())
456             return;
457
458         auto range = capabilities.sampleSize();
459         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleSize(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleSize);
460         break;
461     }
462
463     case MediaConstraintType::EchoCancellation: {
464         ASSERT(constraint.isBoolean());
465         if (!capabilities.supportsEchoCancellation())
466             return;
467
468         bool setting;
469         const BooleanConstraint& boolConstraint = downcast<BooleanConstraint>(constraint);
470         if (boolConstraint.getExact(setting) || boolConstraint.getIdeal(setting))
471             setEchoCancellation(setting);
472         break;
473     }
474
475     case MediaConstraintType::FacingMode: {
476         ASSERT(constraint.isString());
477         if (!capabilities.supportsFacingMode())
478             return;
479
480         auto& supportedModes = capabilities.facingMode();
481         auto filter = [supportedModes](const String& modeString) {
482             auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
483             for (auto& supportedMode : supportedModes) {
484                 if (mode == supportedMode)
485                     return true;
486             }
487             return false;
488         };
489
490         auto modeString = downcast<StringConstraint>(constraint).find(filter);
491         if (!modeString.isEmpty())
492             setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString));
493         break;
494     }
495
496     case MediaConstraintType::DeviceId:
497     case MediaConstraintType::GroupId:
498         ASSERT(constraint.isString());
499         // There is nothing to do here, neither can be changed.
500         break;
501
502     case MediaConstraintType::Unknown:
503         break;
504     }
505 }
506
507 bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
508 {
509     m_fitnessScore = std::numeric_limits<double>::infinity();
510
511     // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
512     //
513     // 1. Each constraint specifies one or more values (or a range of values) for its property.
514     //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
515     //    empty object or list has been given as the value for a constraint, it must be interpreted
516     //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
517     //
518     //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
519     //    constraints will silently disappear. To avoid this being a surprise, application authors are
520     //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
521
522     // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
523     //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
524     //    removed.)
525
526     // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
527     //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
528     //    distance is finite.
529
530     failedConstraint = emptyString();
531
532     // Check width, height, and frame rate separately, because while they may be supported individually the combination may not be supported.
533     double distance = std::numeric_limits<double>::infinity();
534     if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints().width(), constraints.mandatoryConstraints().height(), constraints.mandatoryConstraints().frameRate(), failedConstraint, m_fitnessScore))
535         return false;
536
537     constraints.mandatoryConstraints().filter([&](const MediaConstraint& constraint) {
538         if (!supportsConstraint(constraint))
539             return false;
540
541         if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate) {
542             candidates.set(constraint);
543             return false;
544         }
545
546         double constraintDistance = fitnessDistance(constraint);
547         if (std::isinf(constraintDistance)) {
548             failedConstraint = constraint.name();
549             return true;
550         }
551
552         distance = std::min(distance, constraintDistance);
553         candidates.set(constraint);
554         return false;
555     });
556
557     if (!failedConstraint.isEmpty())
558         return false;
559
560     m_fitnessScore = distance;
561
562     // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
563     if (candidates.isEmpty())
564         return true;
565
566     // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
567     //    For each ConstraintSet:
568
569     // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
570     //     values of properties as exact.
571     Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
572
573     for (const auto& advancedConstraint : constraints.advancedConstraints()) {
574         double constraintDistance = 0;
575         bool supported = false;
576
577         advancedConstraint.forEach([&](const MediaConstraint& constraint) {
578             distance = fitnessDistance(constraint);
579             constraintDistance += distance;
580             if (!std::isinf(distance))
581                 supported = true;
582         });
583
584         m_fitnessScore = std::min(m_fitnessScore, constraintDistance);
585
586         // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
587         //     settings dictionaries in candidates, discarding others.
588         //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
589         if (supported)
590             supportedConstraints.append({constraintDistance, advancedConstraint});
591     }
592
593     // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
594     //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
595     if (!supportedConstraints.isEmpty()) {
596         supportedConstraints.removeAllMatching([&](const std::pair<double, MediaTrackConstraintSetMap>& pair) -> bool {
597             return std::isinf(pair.first) || pair.first > m_fitnessScore;
598         });
599
600         if (!supportedConstraints.isEmpty()) {
601             auto& advancedConstraint = supportedConstraints[0].second;
602             advancedConstraint.forEach([&](const MediaConstraint& constraint) {
603                 candidates.merge(constraint);
604             });
605
606             m_fitnessScore = std::min(m_fitnessScore, supportedConstraints[0].first);
607         }
608     }
609
610     return true;
611 }
612
613 bool RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint) const
614 {
615     ASSERT(this->capabilities());
616     RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
617
618     switch (constraint.constraintType()) {
619     case MediaConstraintType::Width:
620         ASSERT(constraint.isInt());
621         return capabilities.supportsWidth();
622         break;
623
624     case MediaConstraintType::Height:
625         ASSERT(constraint.isInt());
626         return capabilities.supportsHeight();
627         break;
628
629     case MediaConstraintType::FrameRate:
630         ASSERT(constraint.isDouble());
631         return capabilities.supportsFrameRate();
632         break;
633
634     case MediaConstraintType::AspectRatio:
635         ASSERT(constraint.isDouble());
636         return capabilities.supportsAspectRatio();
637         break;
638
639     case MediaConstraintType::Volume:
640         ASSERT(constraint.isDouble());
641         return capabilities.supportsVolume();
642         break;
643
644     case MediaConstraintType::SampleRate:
645         ASSERT(constraint.isInt());
646         return capabilities.supportsSampleRate();
647         break;
648
649     case MediaConstraintType::SampleSize:
650         ASSERT(constraint.isInt());
651         return capabilities.supportsSampleSize();
652         break;
653
654     case MediaConstraintType::FacingMode:
655         ASSERT(constraint.isString());
656         return capabilities.supportsFacingMode();
657         break;
658
659     case MediaConstraintType::EchoCancellation:
660         ASSERT(constraint.isBoolean());
661         return capabilities.supportsEchoCancellation();
662         break;
663
664     case MediaConstraintType::DeviceId:
665         ASSERT(constraint.isString());
666         return capabilities.supportsDeviceId();
667         break;
668
669     case MediaConstraintType::GroupId:
670         ASSERT(constraint.isString());
671         return capabilities.supportsDeviceId();
672         break;
673
674     case MediaConstraintType::Unknown:
675         // Unknown (or unsupported) constraints should be ignored.
676         break;
677     }
678     
679     return false;
680 }
681
682 bool RealtimeMediaSource::supportsConstraints(const MediaConstraints& constraints, String& invalidConstraint)
683 {
684     ASSERT(constraints.isValid());
685
686     FlattenedConstraint candidates;
687     if (!selectSettings(constraints, candidates, invalidConstraint))
688         return false;
689     
690     return true;
691 }
692
693 void RealtimeMediaSource::applyConstraints(const FlattenedConstraint& constraints)
694 {
695     if (constraints.isEmpty())
696         return;
697
698     beginConfiguration();
699
700     RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
701
702     std::optional<int> width;
703     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Width)) {
704         ASSERT(constraint->isInt());
705         if (capabilities.supportsWidth()) {
706             auto range = capabilities.width();
707             width = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
708         }
709     }
710
711     std::optional<int> height;
712     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Height)) {
713         ASSERT(constraint->isInt());
714         if (capabilities.supportsHeight()) {
715             auto range = capabilities.height();
716             height = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
717         }
718     }
719
720     std::optional<double> frameRate;
721     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::FrameRate)) {
722         ASSERT(constraint->isDouble());
723         if (capabilities.supportsFrameRate()) {
724             auto range = capabilities.frameRate();
725             frameRate = downcast<DoubleConstraint>(*constraint).valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
726         }
727     }
728
729     // FIXME: applySizeAndFrameRate should take MediaConstraint* instead of std::optional<> so it can see if a constraint is an exact, min, max,
730     // or ideal, and choose the correct value for properties with non-discreet capabilities when necessary.
731     if (width || height || frameRate)
732         applySizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate));
733
734     for (auto& variant : constraints) {
735         if (variant.constraintType() == MediaConstraintType::Width || variant.constraintType() == MediaConstraintType::Height || variant.constraintType() == MediaConstraintType::FrameRate)
736             continue;
737
738         applyConstraint(variant);
739     }
740
741     commitConfiguration();
742 }
743
744 std::optional<std::pair<String, String>> RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints)
745 {
746     ASSERT(constraints.isValid());
747
748     FlattenedConstraint candidates;
749     String failedConstraint;
750     if (!selectSettings(constraints, candidates, failedConstraint))
751         return { { failedConstraint, ASCIILiteral("Constraint not supported") } };
752
753     applyConstraints(candidates);
754     return std::nullopt;
755 }
756
757 void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
758 {
759     auto result = applyConstraints(constraints);
760     if (!result && successHandler)
761         successHandler();
762     else if (result && failureHandler)
763         failureHandler(result.value().first, result.value().second);
764 }
765
766 void RealtimeMediaSource::setWidth(int width)
767 {
768     if (width == m_size.width())
769         return;
770
771     int height = m_aspectRatio ? width / m_aspectRatio : m_size.height();
772     if (!applySize(IntSize(width, height)))
773         return;
774
775     m_size.setWidth(width);
776     if (m_aspectRatio)
777         m_size.setHeight(width / m_aspectRatio);
778
779     settingsDidChange();
780 }
781
782 void RealtimeMediaSource::setHeight(int height)
783 {
784     if (height == m_size.height())
785         return;
786
787     int width = m_aspectRatio ? height * m_aspectRatio : m_size.width();
788     if (!applySize(IntSize(width, height)))
789         return;
790
791     if (m_aspectRatio)
792         m_size.setWidth(width);
793     m_size.setHeight(height);
794
795     settingsDidChange();
796 }
797
798 void RealtimeMediaSource::setFrameRate(double rate)
799 {
800     if (m_frameRate == rate || !applyFrameRate(rate))
801         return;
802
803     m_frameRate = rate;
804     settingsDidChange();
805 }
806
807 void RealtimeMediaSource::setAspectRatio(double ratio)
808 {
809     if (m_aspectRatio == ratio || !applyAspectRatio(ratio))
810         return;
811
812     m_aspectRatio = ratio;
813     m_size.setHeight(m_size.width() / ratio);
814     settingsDidChange();
815 }
816
817 void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
818 {
819     if (m_facingMode == mode || !applyFacingMode(mode))
820         return;
821
822     m_facingMode = mode;
823     settingsDidChange();
824 }
825
826 void RealtimeMediaSource::setVolume(double volume)
827 {
828     if (m_volume == volume || !applyVolume(volume))
829         return;
830
831     m_volume = volume;
832     settingsDidChange();
833 }
834
835 void RealtimeMediaSource::setSampleRate(int rate)
836 {
837     if (m_sampleRate == rate || !applySampleRate(rate))
838         return;
839
840     m_sampleRate = rate;
841     settingsDidChange();
842 }
843
844 void RealtimeMediaSource::setSampleSize(int size)
845 {
846     if (m_sampleSize == size || !applySampleSize(size))
847         return;
848
849     m_sampleSize = size;
850     settingsDidChange();
851 }
852
853 void RealtimeMediaSource::setEchoCancellation(bool echoCancellation)
854 {
855     if (m_echoCancellation == echoCancellation || !applyEchoCancellation(echoCancellation))
856         return;
857
858     m_echoCancellation = echoCancellation;
859     settingsDidChange();
860 }
861
862 void RealtimeMediaSource::scheduleDeferredTask(std::function<void()>&& function)
863 {
864     ASSERT(function);
865     callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] {
866         if (!weakThis)
867             return;
868
869         function();
870     });
871 }
872
873 } // namespace WebCore
874
875 #endif // ENABLE(MEDIA_STREAM)