f5db9b097e76439ab1198e7ee62be05a22d3d7ab
[WebKit-https.git] / Source / WebCore / platform / mediastream / RealtimeVideoSource.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "RealtimeVideoSource.h"
28
29 #if ENABLE(MEDIA_STREAM)
30 #include "CaptureDevice.h"
31 #include "Logging.h"
32 #include "RealtimeMediaSourceCenter.h"
33 #include "RealtimeMediaSourceSettings.h"
34
35 namespace WebCore {
36
37 RealtimeVideoSource::RealtimeVideoSource(const String& id, const String& name)
38     : RealtimeMediaSource(id, Type::Video, name)
39 {
40 }
41
42 RealtimeVideoSource::~RealtimeVideoSource()
43 {
44 #if PLATFORM(IOS)
45     RealtimeMediaSourceCenter::singleton().videoFactory().unsetActiveSource(*this);
46 #endif
47 }
48
49 void RealtimeVideoSource::prepareToProduceData()
50 {
51     ASSERT(frameRate());
52
53 #if PLATFORM(IOS)
54     RealtimeMediaSourceCenter::singleton().videoFactory().setActiveSource(*this);
55 #endif
56
57     if (size().isEmpty() && !m_defaultSize.isEmpty())
58         setSize(m_defaultSize);
59 }
60
61 const Vector<Ref<VideoPreset>>& RealtimeVideoSource::presets()
62 {
63     if (m_presets.isEmpty())
64         generatePresets();
65
66     ASSERT(!m_presets.isEmpty());
67     return m_presets;
68 }
69
70 void RealtimeVideoSource::setSupportedPresets(Vector<VideoPresetData>&& presetData)
71 {
72     Vector<Ref<VideoPreset>> presets;
73
74     for (auto& data : presetData)
75         presets.append(VideoPreset::create(WTFMove(data)));
76
77     setSupportedPresets(WTFMove(presets));
78 }
79
80 void RealtimeVideoSource::setSupportedPresets(const Vector<Ref<VideoPreset>>& presets)
81 {
82     m_presets = WTF::map(presets, [](auto& preset) {
83         return preset.copyRef();
84     });
85
86     for (auto& preset : m_presets) {
87         std::sort(preset->frameRateRanges.begin(), preset->frameRateRanges.end(),
88             [&] (const auto& a, const auto& b) -> bool {
89                 return a.minimum < b.minimum;
90         });
91     }
92 }
93
94 static const Vector<IntSize>& standardVideoSizes()
95 {
96     static const auto sizes = makeNeverDestroyed([] {
97         static IntSize videoSizes[] = {
98             { 112, 112 },
99             { 160, 160 },
100             { 160, 120 }, // 4:3, QQVGA
101             { 192, 192 },
102             { 192, 112 }, // 16:9
103             { 192, 144 }, // 3:4
104             { 240, 240 },
105             { 240, 160 }, // 3:2, HQVGA
106             { 320, 320 },
107             { 320, 176 }, // 16:9
108             { 320, 240 }, // 4:3, QVGA
109             { 352, 288 }, // CIF
110             { 480, 272 }, // 16:9
111             { 480, 360 }, // 4:3
112             { 480, 480 },
113             { 640, 640 },
114             { 640, 368 }, // 16:9
115             { 640, 480 }, // 4:3
116             { 720, 720 },
117             { 800, 600 }, // 4:3, SVGA
118             { 960, 540 }, // 16:9, qHD
119             { 1024, 600 }, // 16:9, WSVGA
120             { 1024, 768 }, // 4:3, XGA
121             { 1280, 960 }, // 4:3
122             { 1280, 1024 }, // 5:4, SXGA
123             { 1280, 720 }, // 16:9, WXGA
124             { 1366, 768 }, // 16:9, HD
125             { 1920, 1080 }, // 16:9, FHD
126             { 2560, 1440 }, // 16:9, QHD
127             { 2592, 1936 },
128             { 3264, 2448 }, // 3:4
129             { 3840, 2160 }, // 16:9, 4K UHD
130         };
131         Vector<IntSize> sizes;
132         for (auto& size : videoSizes)
133             sizes.append(size);
134
135         return sizes;
136     }());
137
138     return sizes.get();
139 }
140 template <typename ValueType>
141 static void updateMinMax(ValueType& min, ValueType& max, ValueType value)
142 {
143     min = std::min<ValueType>(min, value);
144     max = std::max<ValueType>(max, value);
145 }
146
147 void RealtimeVideoSource::updateCapabilities(RealtimeMediaSourceCapabilities& capabilities)
148 {
149     ASSERT(!presets().isEmpty());
150
151     int minimumWidth = std::numeric_limits<int>::max();
152     int maximumWidth = 0;
153     int minimumHeight = std::numeric_limits<int>::max();
154     int maximumHeight = 0;
155     double minimumAspectRatio = std::numeric_limits<double>::max();
156     double maximumAspectRatio = 0;
157     double minimumFrameRate = std::numeric_limits<double>::max();
158     double maximumFrameRate = 0;
159     for (const auto& preset : presets()) {
160         const auto& size = preset->size;
161         updateMinMax(minimumWidth, maximumWidth, size.width());
162         updateMinMax(minimumHeight, maximumHeight, size.height());
163         updateMinMax(minimumAspectRatio, maximumAspectRatio, static_cast<double>(size.width()) / size.height());
164
165         for (const auto& rate : preset->frameRateRanges) {
166             updateMinMax(minimumFrameRate, maximumFrameRate, rate.minimum);
167             updateMinMax(minimumFrameRate, maximumFrameRate, rate.maximum);
168         }
169     }
170
171     if (canResizeVideoFrames()) {
172         for (auto& size : standardVideoSizes()) {
173             if (size.width() < minimumWidth || size.height() < minimumHeight) {
174                 minimumWidth = std::min(minimumWidth, size.width());
175                 minimumHeight = std::min(minimumHeight, size.height());
176                 minimumAspectRatio = std::min(minimumAspectRatio, static_cast<double>(size.width()) / size.height());
177             }
178         }
179     }
180
181     capabilities.setWidth({ minimumWidth, maximumWidth });
182     capabilities.setHeight({ minimumHeight, maximumHeight });
183     capabilities.setAspectRatio({ minimumAspectRatio, maximumAspectRatio });
184     capabilities.setFrameRate({ minimumFrameRate, maximumFrameRate });
185 }
186
187 bool RealtimeVideoSource::supportsSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
188 {
189     if (!width && !height && !frameRate)
190         return true;
191
192     return !!bestSupportedSizeAndFrameRate(width, height, frameRate);
193 }
194
195 bool RealtimeVideoSource::frameRateRangeIncludesRate(const FrameRateRange& range, double frameRate)
196 {
197     const double epsilon = 0.001;
198     return frameRate + epsilon >= range.minimum && frameRate - epsilon <= range.maximum;
199 }
200
201 bool RealtimeVideoSource::presetSupportsFrameRate(RefPtr<VideoPreset> preset, double frameRate)
202 {
203     for (const auto& range : preset->frameRateRanges) {
204         if (frameRateRangeIncludesRate(range, frameRate))
205             return true;
206     }
207
208     return false;
209 }
210
211 bool RealtimeVideoSource::supportsCaptureSize(std::optional<int> width, std::optional<int> height, const Function<bool(const IntSize&)>&& function)
212 {
213     if (width && height)
214         return function({ width.value(), height.value() });
215
216     if (width) {
217         for (auto& size : standardVideoSizes()) {
218             if (width.value() == size.width() && function({ size.width(), size.height() }))
219                 return true;
220         }
221
222         return false;
223     }
224
225     for (auto& size : standardVideoSizes()) {
226         if (height.value() == size.height() && function({ size.width(), size.height() }))
227             return true;
228     }
229
230     return false;
231 }
232
233 bool RealtimeVideoSource::shouldUsePreset(VideoPreset& current, VideoPreset& candidate)
234 {
235     return candidate.size.width() <= current.size.width() && candidate.size.height() <= current.size.height() && prefersPreset(candidate);
236 }
237
238 std::optional<RealtimeVideoSource::CaptureSizeAndFrameRate> RealtimeVideoSource::bestSupportedSizeAndFrameRate(std::optional<int> requestedWidth, std::optional<int> requestedHeight, std::optional<double> requestedFrameRate)
239 {
240     if (!requestedWidth && !requestedHeight && !requestedFrameRate)
241         return { };
242
243     if (!requestedWidth && !requestedHeight && !size().isEmpty()) {
244         requestedWidth = size().width();
245         requestedHeight = size().height();
246     }
247     if (!requestedFrameRate)
248         requestedFrameRate = frameRate();
249
250     CaptureSizeAndFrameRate result;
251     RefPtr<VideoPreset> exactSizePreset;
252     RefPtr<VideoPreset> aspectRatioPreset;
253     IntSize aspectRatioMatchSize;
254     RefPtr<VideoPreset> resizePreset;
255     IntSize resizeSize;
256
257     for (const auto& preset : presets()) {
258         const auto& presetSize = preset->size;
259
260         if (!presetSupportsFrameRate(&preset.get(), requestedFrameRate.value()))
261             continue;
262
263         if (!requestedWidth && !requestedHeight) {
264             result.requestedFrameRate = requestedFrameRate.value();
265             return result;
266         }
267
268         // Don't look at presets smaller than the requested resolution because we never want to resize larger.
269         if ((requestedWidth && presetSize.width() < requestedWidth.value()) || (requestedHeight && presetSize.height() < requestedHeight.value()))
270             continue;
271
272         auto lookForExactSizeMatch = [&] (const IntSize& size) -> bool {
273             return preset->size == size;
274         };
275         if (supportsCaptureSize(requestedWidth, requestedHeight, WTFMove(lookForExactSizeMatch))) {
276             if (!exactSizePreset || prefersPreset(preset))
277                 exactSizePreset = &preset.get();
278             continue;
279         }
280
281         IntSize encodingSize;
282         auto lookForAspectRatioMatch = [this, &preset, &encodingSize] (const IntSize& size) -> bool {
283             auto aspectRatio = [] (const IntSize size) -> double {
284                 return size.width() / static_cast<double>(size.height());
285             };
286             if (std::abs(aspectRatio(preset->size) - aspectRatio(size)) > 10e-7 || !canResizeVideoFrames())
287                 return false;
288
289             encodingSize = size;
290             return true;
291         };
292         if (supportsCaptureSize(requestedWidth, requestedHeight, WTFMove(lookForAspectRatioMatch))) {
293             if (!aspectRatioPreset || shouldUsePreset(*aspectRatioPreset, preset)) {
294                 aspectRatioPreset = &preset.get();
295                 aspectRatioMatchSize = encodingSize;
296             }
297         }
298
299         if (requestedWidth && requestedHeight) {
300             const auto& minStandardSize = standardVideoSizes()[0];
301             if (requestedWidth.value() >= minStandardSize.width() && requestedHeight.value() >= minStandardSize.height()) {
302                 if (!resizePreset || shouldUsePreset(*resizePreset, preset)) {
303                     resizePreset = &preset.get();
304                     resizeSize = { requestedWidth.value(), requestedHeight.value() };
305                 }
306             }
307         } else {
308             for (auto& standardSize : standardVideoSizes()) {
309                 if (standardSize.width() > preset->size.width() || standardSize.height() > preset->size.height())
310                     break;
311                 if ((requestedWidth && requestedWidth.value() != standardSize.width()) || (requestedHeight && requestedHeight.value() != standardSize.height()))
312                     continue;
313
314                 if (!resizePreset || shouldUsePreset(*resizePreset, preset)) {
315                     resizePreset = &preset.get();
316                     resizeSize = standardSize;
317                 }
318             }
319         }
320     }
321
322     if (!exactSizePreset && !aspectRatioPreset && !resizePreset)
323         return { };
324
325     result.requestedFrameRate = requestedFrameRate.value();
326     if (exactSizePreset) {
327         result.encodingPreset = exactSizePreset;
328         result.requestedSize = exactSizePreset->size;
329         return result;
330     }
331
332     if (aspectRatioPreset) {
333         result.encodingPreset = aspectRatioPreset;
334         result.requestedSize = aspectRatioMatchSize;
335         return result;
336     }
337
338     result.encodingPreset = resizePreset;
339     result.requestedSize = resizeSize;
340     return result;
341 }
342
343 void RealtimeVideoSource::setSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
344 {
345     std::optional<RealtimeVideoSource::CaptureSizeAndFrameRate> match;
346
347     auto size = this->size();
348     if (!width && !height && !size.isEmpty()) {
349         width = size.width();
350         height = size.height();
351     }
352
353     match = bestSupportedSizeAndFrameRate(width, height, frameRate);
354     ASSERT(match);
355     if (!match)
356         return;
357
358     setSizeAndFrameRateWithPreset(match->requestedSize, match->requestedFrameRate, match->encodingPreset);
359
360     if (!match->requestedSize.isEmpty())
361         setSize(match->requestedSize);
362     setFrameRate(match->requestedFrameRate);
363 }
364
365 void RealtimeVideoSource::dispatchMediaSampleToObservers(MediaSample& sample)
366 {
367     MediaTime sampleTime = sample.outputPresentationTime();
368     if (!sampleTime || !sampleTime.isValid())
369         sampleTime = sample.presentationTime();
370
371     auto frameTime = sampleTime.toDouble();
372     m_observedFrameTimeStamps.append(frameTime);
373     m_observedFrameTimeStamps.removeAllMatching([&](auto time) {
374         return time <= frameTime - 2;
375     });
376
377     auto interval = m_observedFrameTimeStamps.last() - m_observedFrameTimeStamps.first();
378     if (interval > 1)
379         m_observedFrameRate = (m_observedFrameTimeStamps.size() / interval);
380
381     videoSampleAvailable(sample);
382 }
383
384 } // namespace WebCore
385
386 #endif // ENABLE(MEDIA_STREAM)