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