0b4a6ed1c8e1557a841e241ddb7e3ea267357301
[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 #include "RemoteVideoSample.h"
35
36 #if PLATFORM(COCOA)
37 #include "ImageTransferSessionVT.h"
38 #endif
39
40 namespace WebCore {
41
42 RealtimeVideoSource::RealtimeVideoSource(String&& name, String&& id, String&& hashSalt)
43     : RealtimeMediaSource(Type::Video, WTFMove(name), WTFMove(id), WTFMove(hashSalt))
44 {
45 }
46
47 RealtimeVideoSource::~RealtimeVideoSource()
48 {
49 #if PLATFORM(IOS_FAMILY)
50     RealtimeMediaSourceCenter::singleton().videoFactory().unsetActiveSource(*this);
51 #endif
52 }
53
54 void RealtimeVideoSource::prepareToProduceData()
55 {
56     ASSERT(frameRate());
57
58 #if PLATFORM(IOS_FAMILY)
59     RealtimeMediaSourceCenter::singleton().videoFactory().setActiveSource(*this);
60 #endif
61
62     if (size().isEmpty() && !m_defaultSize.isEmpty())
63         setSize(m_defaultSize);
64 }
65
66 const Vector<Ref<VideoPreset>>& RealtimeVideoSource::presets()
67 {
68     if (m_presets.isEmpty())
69         generatePresets();
70
71     ASSERT(!m_presets.isEmpty());
72     return m_presets;
73 }
74
75 void RealtimeVideoSource::setSupportedPresets(Vector<VideoPresetData>&& presetData)
76 {
77     Vector<Ref<VideoPreset>> presets;
78
79     for (auto& data : presetData)
80         presets.append(VideoPreset::create(WTFMove(data)));
81
82     setSupportedPresets(WTFMove(presets));
83 }
84
85 void RealtimeVideoSource::setSupportedPresets(const Vector<Ref<VideoPreset>>& presets)
86 {
87     m_presets = WTF::map(presets, [](auto& preset) {
88         return preset.copyRef();
89     });
90
91     for (auto& preset : m_presets) {
92         std::sort(preset->frameRateRanges.begin(), preset->frameRateRanges.end(),
93             [&] (const auto& a, const auto& b) -> bool {
94                 return a.minimum < b.minimum;
95         });
96     }
97 }
98
99 const Vector<IntSize>& RealtimeVideoSource::standardVideoSizes()
100 {
101     static const auto sizes = makeNeverDestroyed([] {
102         static IntSize videoSizes[] = {
103             { 112, 112 },
104             { 160, 160 },
105             { 160, 120 }, // 4:3, QQVGA
106             { 192, 192 },
107             { 192, 112 }, // 16:9
108             { 192, 144 }, // 3:4
109             { 240, 240 },
110             { 240, 160 }, // 3:2, HQVGA
111             { 320, 320 },
112             { 320, 176 }, // 16:9
113             { 320, 240 }, // 4:3, QVGA
114             { 352, 288 }, // CIF
115             { 480, 272 }, // 16:9
116             { 480, 360 }, // 4:3
117             { 480, 480 },
118             { 640, 640 },
119             { 640, 368 }, // 16:9
120             { 640, 480 }, // 4:3
121             { 720, 720 },
122             { 800, 600 }, // 4:3, SVGA
123             { 960, 540 }, // 16:9, qHD
124             { 1024, 600 }, // 16:9, WSVGA
125             { 1024, 768 }, // 4:3, XGA
126             { 1280, 960 }, // 4:3
127             { 1280, 1024 }, // 5:4, SXGA
128             { 1280, 720 }, // 16:9, WXGA
129             { 1366, 768 }, // 16:9, HD
130             { 1920, 1080 }, // 16:9, FHD
131             { 2560, 1440 }, // 16:9, QHD
132             { 2592, 1936 },
133             { 3264, 2448 }, // 3:4
134             { 3840, 2160 }, // 16:9, 4K UHD
135         };
136         Vector<IntSize> sizes;
137         for (auto& size : videoSizes)
138             sizes.append(size);
139
140         return sizes;
141     }());
142
143     return sizes.get();
144 }
145 template <typename ValueType>
146 static void updateMinMax(ValueType& min, ValueType& max, ValueType value)
147 {
148     min = std::min<ValueType>(min, value);
149     max = std::max<ValueType>(max, value);
150 }
151
152 void RealtimeVideoSource::updateCapabilities(RealtimeMediaSourceCapabilities& capabilities)
153 {
154     ASSERT(!presets().isEmpty());
155
156     int minimumWidth = std::numeric_limits<int>::max();
157     int maximumWidth = 0;
158     int minimumHeight = std::numeric_limits<int>::max();
159     int maximumHeight = 0;
160     double minimumAspectRatio = std::numeric_limits<double>::max();
161     double maximumAspectRatio = 0;
162     double minimumFrameRate = std::numeric_limits<double>::max();
163     double maximumFrameRate = 0;
164     for (const auto& preset : presets()) {
165         const auto& size = preset->size;
166         updateMinMax(minimumWidth, maximumWidth, size.width());
167         updateMinMax(minimumHeight, maximumHeight, size.height());
168         updateMinMax(minimumAspectRatio, maximumAspectRatio, static_cast<double>(size.width()) / size.height());
169
170         for (const auto& rate : preset->frameRateRanges) {
171             updateMinMax(minimumFrameRate, maximumFrameRate, rate.minimum);
172             updateMinMax(minimumFrameRate, maximumFrameRate, rate.maximum);
173         }
174     }
175
176     if (canResizeVideoFrames()) {
177         for (auto& size : standardVideoSizes()) {
178             if (size.width() < minimumWidth || size.height() < minimumHeight) {
179                 minimumWidth = std::min(minimumWidth, size.width());
180                 minimumHeight = std::min(minimumHeight, size.height());
181                 minimumAspectRatio = std::min(minimumAspectRatio, static_cast<double>(size.width()) / size.height());
182             }
183         }
184     }
185
186     capabilities.setWidth({ minimumWidth, maximumWidth });
187     capabilities.setHeight({ minimumHeight, maximumHeight });
188     capabilities.setAspectRatio({ minimumAspectRatio, maximumAspectRatio });
189     capabilities.setFrameRate({ minimumFrameRate, maximumFrameRate });
190 }
191
192 bool RealtimeVideoSource::supportsSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
193 {
194     if (!width && !height && !frameRate)
195         return true;
196
197     return !!bestSupportedSizeAndFrameRate(width, height, frameRate);
198 }
199
200 bool RealtimeVideoSource::frameRateRangeIncludesRate(const FrameRateRange& range, double frameRate)
201 {
202     const double epsilon = 0.001;
203     return frameRate + epsilon >= range.minimum && frameRate - epsilon <= range.maximum;
204 }
205
206 bool RealtimeVideoSource::presetSupportsFrameRate(RefPtr<VideoPreset> preset, double frameRate)
207 {
208     for (const auto& range : preset->frameRateRanges) {
209         if (frameRateRangeIncludesRate(range, frameRate))
210             return true;
211     }
212
213     return false;
214 }
215
216 bool RealtimeVideoSource::supportsCaptureSize(std::optional<int> width, std::optional<int> height, const Function<bool(const IntSize&)>&& function)
217 {
218     if (width && height)
219         return function({ width.value(), height.value() });
220
221     if (width) {
222         for (auto& size : standardVideoSizes()) {
223             if (width.value() == size.width() && function({ size.width(), size.height() }))
224                 return true;
225         }
226
227         return false;
228     }
229
230     for (auto& size : standardVideoSizes()) {
231         if (height.value() == size.height() && function({ size.width(), size.height() }))
232             return true;
233     }
234
235     return false;
236 }
237
238 bool RealtimeVideoSource::shouldUsePreset(VideoPreset& current, VideoPreset& candidate)
239 {
240     return candidate.size.width() <= current.size.width() && candidate.size.height() <= current.size.height() && prefersPreset(candidate);
241 }
242
243 std::optional<RealtimeVideoSource::CaptureSizeAndFrameRate> RealtimeVideoSource::bestSupportedSizeAndFrameRate(std::optional<int> requestedWidth, std::optional<int> requestedHeight, std::optional<double> requestedFrameRate)
244 {
245     if (!requestedWidth && !requestedHeight && !requestedFrameRate)
246         return { };
247
248     if (!requestedWidth && !requestedHeight && !size().isEmpty()) {
249         requestedWidth = size().width();
250         requestedHeight = size().height();
251     }
252     if (!requestedFrameRate)
253         requestedFrameRate = frameRate();
254
255     CaptureSizeAndFrameRate result;
256     RefPtr<VideoPreset> exactSizePreset;
257     RefPtr<VideoPreset> aspectRatioPreset;
258     IntSize aspectRatioMatchSize;
259     RefPtr<VideoPreset> resizePreset;
260     IntSize resizeSize;
261
262     for (const auto& preset : presets()) {
263         const auto& presetSize = preset->size;
264
265         if (!presetSupportsFrameRate(&preset.get(), requestedFrameRate.value()))
266             continue;
267
268         if (!requestedWidth && !requestedHeight) {
269             result.requestedFrameRate = requestedFrameRate.value();
270             return result;
271         }
272
273         // Don't look at presets smaller than the requested resolution because we never want to resize larger.
274         if ((requestedWidth && presetSize.width() < requestedWidth.value()) || (requestedHeight && presetSize.height() < requestedHeight.value()))
275             continue;
276
277         auto lookForExactSizeMatch = [&] (const IntSize& size) -> bool {
278             return preset->size == size;
279         };
280         if (supportsCaptureSize(requestedWidth, requestedHeight, WTFMove(lookForExactSizeMatch))) {
281             if (!exactSizePreset || prefersPreset(preset))
282                 exactSizePreset = &preset.get();
283             continue;
284         }
285
286         IntSize encodingSize;
287         auto lookForAspectRatioMatch = [this, &preset, &encodingSize] (const IntSize& size) -> bool {
288             auto aspectRatio = [] (const IntSize size) -> double {
289                 return size.width() / static_cast<double>(size.height());
290             };
291             if (std::abs(aspectRatio(preset->size) - aspectRatio(size)) > 10e-7 || !canResizeVideoFrames())
292                 return false;
293
294             encodingSize = size;
295             return true;
296         };
297         if (supportsCaptureSize(requestedWidth, requestedHeight, WTFMove(lookForAspectRatioMatch))) {
298             if (!aspectRatioPreset || shouldUsePreset(*aspectRatioPreset, preset)) {
299                 aspectRatioPreset = &preset.get();
300                 aspectRatioMatchSize = encodingSize;
301             }
302         }
303
304         if (requestedWidth && requestedHeight) {
305             const auto& minStandardSize = standardVideoSizes()[0];
306             if (requestedWidth.value() >= minStandardSize.width() && requestedHeight.value() >= minStandardSize.height()) {
307                 if (!resizePreset || shouldUsePreset(*resizePreset, preset)) {
308                     resizePreset = &preset.get();
309                     resizeSize = { requestedWidth.value(), requestedHeight.value() };
310                 }
311             }
312         } else {
313             for (auto& standardSize : standardVideoSizes()) {
314                 if (standardSize.width() > preset->size.width() || standardSize.height() > preset->size.height())
315                     break;
316                 if ((requestedWidth && requestedWidth.value() != standardSize.width()) || (requestedHeight && requestedHeight.value() != standardSize.height()))
317                     continue;
318
319                 if (!resizePreset || shouldUsePreset(*resizePreset, preset)) {
320                     resizePreset = &preset.get();
321                     resizeSize = standardSize;
322                 }
323             }
324         }
325     }
326
327     if (!exactSizePreset && !aspectRatioPreset && !resizePreset)
328         return { };
329
330     result.requestedFrameRate = requestedFrameRate.value();
331     if (exactSizePreset) {
332         result.encodingPreset = exactSizePreset;
333         result.requestedSize = exactSizePreset->size;
334         return result;
335     }
336
337     if (aspectRatioPreset) {
338         result.encodingPreset = aspectRatioPreset;
339         result.requestedSize = aspectRatioMatchSize;
340         return result;
341     }
342
343     result.encodingPreset = resizePreset;
344     result.requestedSize = resizeSize;
345     return result;
346 }
347
348 void RealtimeVideoSource::setSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
349 {
350     std::optional<RealtimeVideoSource::CaptureSizeAndFrameRate> match;
351
352     auto size = this->size();
353     if (!width && !height && !size.isEmpty()) {
354         width = size.width();
355         height = size.height();
356     }
357
358     match = bestSupportedSizeAndFrameRate(width, height, frameRate);
359     ASSERT(match);
360     if (!match)
361         return;
362
363     setSizeAndFrameRateWithPreset(match->requestedSize, match->requestedFrameRate, match->encodingPreset);
364
365     if (!match->requestedSize.isEmpty())
366         setSize(match->requestedSize);
367     setFrameRate(match->requestedFrameRate);
368 }
369
370 void RealtimeVideoSource::dispatchMediaSampleToObservers(MediaSample& sample)
371 {
372     MediaTime sampleTime = sample.outputPresentationTime();
373     if (!sampleTime || !sampleTime.isValid())
374         sampleTime = sample.presentationTime();
375
376     auto frameTime = sampleTime.toDouble();
377     m_observedFrameTimeStamps.append(frameTime);
378     m_observedFrameTimeStamps.removeAllMatching([&](auto time) {
379         return time <= frameTime - 2;
380     });
381
382     auto interval = m_observedFrameTimeStamps.last() - m_observedFrameTimeStamps.first();
383     if (interval > 1)
384         m_observedFrameRate = (m_observedFrameTimeStamps.size() / interval);
385
386     if (isRemote()) {
387 #if HAVE(IOSURFACE)
388         auto remoteSample = RemoteVideoSample::create(WTFMove(sample));
389         if (remoteSample)
390             remoteVideoSampleAvailable(WTFMove(*remoteSample));
391 #else
392         ASSERT_NOT_REACHED();
393 #endif
394         return;
395     }
396
397     auto mediaSample = makeRefPtr(&sample);
398 #if PLATFORM(COCOA)
399     auto size = this->size();
400     if (!size.isEmpty() && size != expandedIntSize(sample.presentationSize())) {
401
402         if (!m_imageTransferSession)
403             m_imageTransferSession = ImageTransferSessionVT::create(sample.videoPixelFormat());
404
405         if (m_imageTransferSession) {
406             mediaSample = m_imageTransferSession->convertMediaSample(sample, size);
407             if (!mediaSample) {
408                 ASSERT_NOT_REACHED();
409                 return;
410             }
411         }
412     }
413 #endif
414
415     videoSampleAvailable(mediaSample.releaseNonNull());
416 }
417
418 } // namespace WebCore
419
420 #endif // ENABLE(MEDIA_STREAM)