A Document / Window should lose its browsing context as soon as its iframe is removed...
[WebKit-https.git] / Source / WebCore / platform / mock / MockRealtimeVideoSource.cpp
1 /*
2  * Copyright (C) 2015-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  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of Google Inc. nor the names of its contributors
15  *    may be used to endorse or promote products derived from this
16  *    software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "MockRealtimeVideoSource.h"
33
34 #if ENABLE(MEDIA_STREAM)
35 #include "CaptureDevice.h"
36 #include "GraphicsContext.h"
37 #include "ImageBuffer.h"
38 #include "IntRect.h"
39 #include "Logging.h"
40 #include "MediaConstraints.h"
41 #include "MockRealtimeMediaSourceCenter.h"
42 #include "NotImplemented.h"
43 #include "PlatformLayer.h"
44 #include "RealtimeMediaSourceSettings.h"
45 #include <math.h>
46 #include <wtf/UUID.h>
47 #include <wtf/text/StringView.h>
48
49 namespace WebCore {
50
51 #if !PLATFORM(MAC) && !PLATFORM(IOS) && !(USE(GSTREAMER) && USE(LIBWEBRTC))
52 CaptureSourceOrError MockRealtimeVideoSource::create(const String& deviceID, const String& name, const MediaConstraints* constraints)
53 {
54     auto device = MockRealtimeMediaSourceCenter::mockDeviceWithPersistentID(deviceID);
55     ASSERT(device);
56     if (!device)
57         return { };
58
59     auto source = adoptRef(*new MockRealtimeVideoSource(WTFMove(device), deviceID, name));
60     if (constraints && source->applyConstraints(*constraints))
61         return { };
62
63     return CaptureSourceOrError(WTFMove(source));
64 }
65 #endif
66
67 MockRealtimeVideoSource::MockRealtimeVideoSource(const String& deviceID, const String& name)
68     : RealtimeVideoSource(deviceID, name)
69     , m_emitFrameTimer(RunLoop::current(), this, &MockRealtimeVideoSource::generateFrame)
70 {
71     auto device = MockRealtimeMediaSourceCenter::mockDeviceWithPersistentID(deviceID);
72     ASSERT(device);
73     m_device = *device;
74
75     m_dashWidths.reserveInitialCapacity(2);
76     m_dashWidths.uncheckedAppend(6);
77     m_dashWidths.uncheckedAppend(6);
78
79     if (mockDisplay()) {
80         auto& properties = WTF::get<MockDisplayProperties>(m_device.properties);
81         setSize(properties.defaultSize);
82         m_fillColor = properties.fillColor;
83         return;
84     }
85
86     auto& properties = WTF::get<MockCameraProperties>(m_device.properties);
87     setFrameRate(properties.defaultFrameRate);
88     setFacingMode(properties.facingMode);
89     m_fillColor = properties.fillColor;
90 }
91
92 bool MockRealtimeVideoSource::supportsSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> rate)
93 {
94     // FIXME: consider splitting mock display into another class so we don't don't have to do this silly dance
95     // because of the RealtimeVideoSource inheritance.
96     if (mockCamera())
97         return RealtimeVideoSource::supportsSizeAndFrameRate(width, height, rate);
98
99     return RealtimeMediaSource::supportsSizeAndFrameRate(width, height, rate);
100 }
101
102 void MockRealtimeVideoSource::setSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> rate)
103 {
104     // FIXME: consider splitting mock display into another class so we don't don't have to do this silly dance
105     // because of the RealtimeVideoSource inheritance.
106     if (mockCamera())
107         return RealtimeVideoSource::setSizeAndFrameRate(width, height, rate);
108
109     return RealtimeMediaSource::setSizeAndFrameRate(width, height, rate);
110 }
111
112 void MockRealtimeVideoSource::generatePresets()
113 {
114     ASSERT(mockCamera());
115     setSupportedPresets(WTFMove(WTF::get<MockCameraProperties>(m_device.properties).presets));
116 }
117
118 const RealtimeMediaSourceCapabilities& MockRealtimeVideoSource::capabilities()
119 {
120     if (!m_capabilities) {
121         RealtimeMediaSourceCapabilities capabilities(settings().supportedConstraints());
122
123         capabilities.setDeviceId(id());
124         if (mockCamera()) {
125             capabilities.addFacingMode(WTF::get<MockCameraProperties>(m_device.properties).facingMode);
126             updateCapabilities(capabilities);
127         } else {
128             capabilities.setWidth(CapabilityValueOrRange(72, 2880));
129             capabilities.setHeight(CapabilityValueOrRange(45, 1800));
130             capabilities.setFrameRate(CapabilityValueOrRange(.01, 60.0));
131         }
132
133         m_capabilities = WTFMove(capabilities);
134     }
135
136     return m_capabilities.value();
137 }
138
139 const RealtimeMediaSourceSettings& MockRealtimeVideoSource::settings()
140 {
141     if (m_currentSettings)
142         return m_currentSettings.value();
143
144     RealtimeMediaSourceSettings settings;
145     if (mockCamera())
146         settings.setFacingMode(facingMode());
147     else {
148         settings.setDisplaySurface(mockScreen() ? RealtimeMediaSourceSettings::DisplaySurfaceType::Monitor : RealtimeMediaSourceSettings::DisplaySurfaceType::Window);
149         settings.setLogicalSurface(false);
150     }
151     settings.setFrameRate(frameRate());
152     auto& size = this->size();
153     settings.setWidth(size.width());
154     settings.setHeight(size.height());
155     if (aspectRatio())
156         settings.setAspectRatio(aspectRatio());
157     settings.setDeviceId(id());
158
159     RealtimeMediaSourceSupportedConstraints supportedConstraints;
160     supportedConstraints.setSupportsDeviceId(true);
161     supportedConstraints.setSupportsFrameRate(true);
162     supportedConstraints.setSupportsWidth(true);
163     supportedConstraints.setSupportsHeight(true);
164     supportedConstraints.setSupportsAspectRatio(true);
165     if (mockCamera())
166         supportedConstraints.setSupportsFacingMode(true);
167     else {
168         supportedConstraints.setSupportsDisplaySurface(true);
169         supportedConstraints.setSupportsLogicalSurface(true);
170     }
171     settings.setSupportedConstraints(supportedConstraints);
172
173     m_currentSettings = WTFMove(settings);
174
175     return m_currentSettings.value();
176 }
177
178 void MockRealtimeVideoSource::settingsDidChange(OptionSet<RealtimeMediaSourceSettings::Flag> settings)
179 {
180     m_currentSettings = std::nullopt;
181     if (settings.containsAny({ RealtimeMediaSourceSettings::Flag::Width, RealtimeMediaSourceSettings::Flag::Height })) {
182         m_baseFontSize = size().height() * .08;
183         m_bipBopFontSize = m_baseFontSize * 2.5;
184         m_statsFontSize = m_baseFontSize * .5;
185         m_imageBuffer = nullptr;
186     }
187 }
188
189 void MockRealtimeVideoSource::startCaptureTimer()
190 {
191     m_emitFrameTimer.startRepeating(1_s / frameRate());
192 }
193
194 void MockRealtimeVideoSource::startProducingData()
195 {
196     prepareToProduceData();
197     startCaptureTimer();
198     m_startTime = MonotonicTime::now();
199 }
200
201 void MockRealtimeVideoSource::stopProducingData()
202 {
203     m_emitFrameTimer.stop();
204     m_elapsedTime += MonotonicTime::now() - m_startTime;
205     m_startTime = MonotonicTime::nan();
206 }
207
208 Seconds MockRealtimeVideoSource::elapsedTime()
209 {
210     if (std::isnan(m_startTime))
211         return m_elapsedTime;
212
213     return m_elapsedTime + (MonotonicTime::now() - m_startTime);
214 }
215
216 void MockRealtimeVideoSource::drawAnimation(GraphicsContext& context)
217 {
218     float radius = size().width() * .09;
219     FloatPoint location(size().width() * .8, size().height() * .3);
220
221     m_path.clear();
222     m_path.moveTo(location);
223     m_path.addArc(location, radius, 0, 2 * piFloat, false);
224     m_path.closeSubpath();
225     context.setFillColor(Color::white);
226     context.setFillRule(WindRule::NonZero);
227     context.fillPath(m_path);
228
229     float endAngle = piFloat * (((fmod(m_frameNumber, frameRate()) + 0.5) * (2.0 / frameRate())) + 1);
230     m_path.clear();
231     m_path.moveTo(location);
232     m_path.addArc(location, radius, 1.5 * piFloat, endAngle, false);
233     m_path.closeSubpath();
234     context.setFillColor(Color::gray);
235     context.setFillRule(WindRule::NonZero);
236     context.fillPath(m_path);
237 }
238
239 void MockRealtimeVideoSource::drawBoxes(GraphicsContext& context)
240 {
241     static const RGBA32 magenta = 0xffff00ff;
242     static const RGBA32 yellow = 0xffffff00;
243     static const RGBA32 blue = 0xff0000ff;
244     static const RGBA32 red = 0xffff0000;
245     static const RGBA32 green = 0xff008000;
246     static const RGBA32 cyan = 0xFF00FFFF;
247
248     IntSize size = this->size();
249     float boxSize = size.width() * .035;
250     float boxTop = size.height() * .6;
251
252     m_path.clear();
253     FloatRect frameRect(2, 2, size.width() - 3, size.height() - 3);
254     context.setStrokeColor(Color::white);
255     context.setStrokeThickness(3);
256     context.setLineDash(m_dashWidths, 0);
257     m_path.addRect(frameRect);
258     m_path.closeSubpath();
259     context.strokePath(m_path);
260
261     context.setLineDash(DashArray(), 0);
262     m_path.clear();
263     m_path.moveTo(FloatPoint(0, boxTop + boxSize));
264     m_path.addLineTo(FloatPoint(size.width(), boxTop + boxSize));
265     m_path.closeSubpath();
266     context.setStrokeColor(Color::white);
267     context.setStrokeThickness(2);
268     context.strokePath(m_path);
269
270     context.setStrokeThickness(1);
271     float boxLeft = boxSize;
272     m_path.clear();
273     for (unsigned i = 0; i < boxSize / 4; i++) {
274         m_path.moveTo(FloatPoint(boxLeft + 4 * i, boxTop));
275         m_path.addLineTo(FloatPoint(boxLeft + 4 * i, boxTop + boxSize));
276     }
277     boxLeft += boxSize + 2;
278     for (unsigned i = 0; i < boxSize / 4; i++) {
279         m_path.moveTo(FloatPoint(boxLeft, boxTop + 4 * i));
280         m_path.addLineTo(FloatPoint(boxLeft + boxSize - 1, boxTop + 4 * i));
281     }
282     context.setStrokeThickness(3);
283     boxLeft += boxSize + 2;
284     for (unsigned i = 0; i < boxSize / 8; i++) {
285         m_path.moveTo(FloatPoint(boxLeft + 8 * i, boxTop));
286         m_path.addLineTo(FloatPoint(boxLeft + 8 * i, boxTop + boxSize - 1));
287     }
288     boxLeft += boxSize + 2;
289     for (unsigned i = 0; i < boxSize / 8; i++) {
290         m_path.moveTo(FloatPoint(boxLeft, boxTop + 8 * i));
291         m_path.addLineTo(FloatPoint(boxLeft + boxSize - 1, boxTop + 8 * i));
292     }
293
294     boxTop += boxSize + 2;
295     boxLeft = boxSize;
296     Color boxColors[] = { Color::white, yellow, cyan, green, magenta, red, blue };
297     for (unsigned i = 0; i < sizeof(boxColors) / sizeof(boxColors[0]); i++) {
298         context.fillRect(FloatRect(boxLeft, boxTop, boxSize + 1, boxSize + 1), boxColors[i]);
299         boxLeft += boxSize + 1;
300     }
301     context.strokePath(m_path);
302 }
303
304 void MockRealtimeVideoSource::drawText(GraphicsContext& context)
305 {
306     unsigned milliseconds = lround(elapsedTime().milliseconds());
307     unsigned seconds = milliseconds / 1000 % 60;
308     unsigned minutes = seconds / 60 % 60;
309     unsigned hours = minutes / 60 % 60;
310
311     FontCascadeDescription fontDescription;
312     fontDescription.setOneFamily("Courier");
313     fontDescription.setWeight(FontSelectionValue(500));
314
315     fontDescription.setSpecifiedSize(m_baseFontSize);
316     fontDescription.setComputedSize(m_baseFontSize);
317     FontCascade timeFont { FontCascadeDescription { fontDescription }, 0, 0 };
318     timeFont.update(nullptr);
319
320     fontDescription.setSpecifiedSize(m_bipBopFontSize);
321     fontDescription.setComputedSize(m_bipBopFontSize);
322     FontCascade bipBopFont { FontCascadeDescription { fontDescription }, 0, 0 };
323     bipBopFont.update(nullptr);
324
325     fontDescription.setSpecifiedSize(m_statsFontSize);
326     fontDescription.setComputedSize(m_statsFontSize);
327     FontCascade statsFont { WTFMove(fontDescription), 0, 0 };
328     statsFont.update(nullptr);
329
330     IntSize size = this->size();
331     FloatPoint timeLocation(size.width() * .05, size.height() * .15);
332     context.setFillColor(Color::white);
333     context.setTextDrawingMode(TextModeFill);
334     String string = String::format("%02u:%02u:%02u.%03u", hours, minutes, seconds, milliseconds % 1000);
335     context.drawText(timeFont, TextRun((StringView(string))), timeLocation);
336
337     string = String::format("%06u", m_frameNumber++);
338     timeLocation.move(0, m_baseFontSize);
339     context.drawText(timeFont, TextRun((StringView(string))), timeLocation);
340
341     FloatPoint statsLocation(size.width() * .45, size.height() * .75);
342     string = String::format("Requested frame rate: %.1f fps", frameRate());
343     context.drawText(statsFont, TextRun((StringView(string))), statsLocation);
344
345     statsLocation.move(0, m_statsFontSize);
346     string = String::format("Observed frame rate: %.1f fps", observedFrameRate());
347     context.drawText(statsFont, TextRun((StringView(string))), statsLocation);
348
349     statsLocation.move(0, m_statsFontSize);
350     string = String::format("Size: %u x %u", size.width(), size.height());
351     context.drawText(statsFont, TextRun((StringView(string))), statsLocation);
352
353     if (mockCamera()) {
354         const char* camera;
355         switch (facingMode()) {
356         case RealtimeMediaSourceSettings::User:
357             camera = "User facing";
358             break;
359         case RealtimeMediaSourceSettings::Environment:
360             camera = "Environment facing";
361             break;
362         case RealtimeMediaSourceSettings::Left:
363             camera = "Left facing";
364             break;
365         case RealtimeMediaSourceSettings::Right:
366             camera = "Right facing";
367             break;
368         case RealtimeMediaSourceSettings::Unknown:
369             camera = "Unknown";
370             break;
371         }
372         string = String::format("Camera: %s", camera);
373         statsLocation.move(0, m_statsFontSize);
374         context.drawText(statsFont, TextRun((StringView(string))), statsLocation);
375     } else if (!name().isNull()) {
376         statsLocation.move(0, m_statsFontSize);
377         context.drawText(statsFont, TextRun { name() }, statsLocation);
378     }
379
380     FloatPoint bipBopLocation(size.width() * .6, size.height() * .6);
381     unsigned frameMod = m_frameNumber % 60;
382     if (frameMod <= 15) {
383         context.setFillColor(Color::cyan);
384         String bip("Bip"_s);
385         context.drawText(bipBopFont, TextRun(StringView(bip)), bipBopLocation);
386     } else if (frameMod > 30 && frameMod <= 45) {
387         context.setFillColor(Color::yellow);
388         String bop("Bop"_s);
389         context.drawText(bipBopFont, TextRun(StringView(bop)), bipBopLocation);
390     }
391 }
392
393 void MockRealtimeVideoSource::delaySamples(Seconds delta)
394 {
395     m_delayUntil = MonotonicTime::now() + delta;
396 }
397
398 void MockRealtimeVideoSource::generateFrame()
399 {
400     if (m_delayUntil) {
401         if (m_delayUntil < MonotonicTime::now())
402             return;
403         m_delayUntil = MonotonicTime();
404     }
405
406     ImageBuffer* buffer = imageBuffer();
407     if (!buffer)
408         return;
409
410     GraphicsContext& context = buffer->context();
411     GraphicsContextStateSaver stateSaver(context);
412
413     auto& size = this->size();
414     FloatRect frameRect(FloatPoint(), size);
415
416     context.fillRect(FloatRect(FloatPoint(), size), m_fillColor);
417
418     if (!muted()) {
419         drawText(context);
420         drawAnimation(context);
421         drawBoxes(context);
422     }
423
424     updateSampleBuffer();
425 }
426
427 ImageBuffer* MockRealtimeVideoSource::imageBuffer() const
428 {
429     if (m_imageBuffer)
430         return m_imageBuffer.get();
431
432     m_imageBuffer = ImageBuffer::create(size(), Unaccelerated);
433     if (!m_imageBuffer)
434         return nullptr;
435
436     m_imageBuffer->context().setImageInterpolationQuality(InterpolationDefault);
437     m_imageBuffer->context().setStrokeThickness(1);
438
439     return m_imageBuffer.get();
440 }
441
442 bool MockRealtimeVideoSource::mockDisplayType(CaptureDevice::DeviceType type) const
443 {
444     if (!WTF::holds_alternative<MockDisplayProperties>(m_device.properties))
445         return false;
446
447     return WTF::get<MockDisplayProperties>(m_device.properties).type == type;
448 }
449
450 } // namespace WebCore
451
452 #endif // ENABLE(MEDIA_STREAM)