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