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