1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
4 * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "MockRealtimeMediaSourceCenter.h"
30
31#if ENABLE(MEDIA_STREAM)
32
33#include "CaptureDevice.h"
34#include "Logging.h"
35#include "MediaConstraints.h"
36#include "MockRealtimeAudioSource.h"
37#include "MockRealtimeVideoSource.h"
38#include "NotImplemented.h"
39#include "RealtimeMediaSourceSettings.h"
40#include <math.h>
41#include <wtf/NeverDestroyed.h>
42#include <wtf/text/StringView.h>
43
44namespace WebCore {
45
46static inline Vector<MockMediaDevice> defaultDevices()
47{
48 return Vector<MockMediaDevice> {
49 MockMediaDevice { "239c24b0-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 1"_s, MockMicrophoneProperties { 44100 } },
50 MockMediaDevice { "239c24b1-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 2"_s, MockMicrophoneProperties { 48000 } },
51
52 MockMediaDevice { "239c24b2-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 1"_s,
53 MockCameraProperties {
54 30,
55 RealtimeMediaSourceSettings::VideoFacingMode::User, {
56 { { 1280, 720 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
57 { { 640, 480 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
58 { { 112, 112 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
59 },
60 Color::black,
61 } },
62
63 MockMediaDevice { "239c24b3-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 2"_s,
64 MockCameraProperties {
65 15,
66 RealtimeMediaSourceSettings::VideoFacingMode::Environment, {
67 { { 3840, 2160 }, { { 2, 30 } } },
68 { { 1920, 1080 }, { { 2, 30 } } },
69 { { 1280, 720 }, { { 3, 120 } } },
70 { { 960, 540 }, { { 3, 60 } } },
71 { { 640, 480 }, { { 2, 30 } } },
72 { { 352, 288 }, { { 2, 30 } } },
73 { { 320, 240 }, { { 2, 30 } } },
74 { { 160, 120 }, { { 2, 30 } } },
75 },
76 Color::darkGray,
77 } },
78
79 MockMediaDevice { "SCREEN-1"_s, "Mock screen device 1"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, Color::lightGray, { 3840, 2160 } } },
80 MockMediaDevice { "SCREEN-2"_s, "Mock screen device 2"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, Color::yellow, { 1920, 1080 } } },
81
82 MockMediaDevice { "WINDOW-2"_s, "Mock window 1"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, 0xfff1b5, { 640, 480 } } },
83 MockMediaDevice { "WINDOW-2"_s, "Mock window 2"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, 0xffd0b5, { 1280, 600 } } },
84 };
85}
86
87class MockRealtimeVideoSourceFactory : public VideoCaptureFactory {
88public:
89 CaptureSourceOrError createVideoCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
90 {
91 ASSERT(device.type() == CaptureDevice::DeviceType::Camera);
92 ASSERT(MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, device.persistentId()));
93
94 return MockRealtimeVideoSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
95 }
96
97private:
98#if PLATFORM(IOS_FAMILY)
99 void setVideoCapturePageState(bool interrupted, bool pageMuted)
100 {
101 if (activeSource())
102 activeSource()->setInterrupted(interrupted, pageMuted);
103 }
104#endif
105 CaptureDeviceManager& videoCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().videoCaptureDeviceManager(); }
106};
107
108class MockRealtimeDisplaySourceFactory : public DisplayCaptureFactory {
109public:
110 CaptureSourceOrError createDisplayCaptureSource(const CaptureDevice& device, const MediaConstraints* constraints) final
111 {
112 ASSERT(MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(device.type(), device.persistentId()));
113
114 switch (device.type()) {
115 case CaptureDevice::DeviceType::Screen:
116 case CaptureDevice::DeviceType::Window:
117 return MockRealtimeVideoSource::create(String { device.persistentId() }, String { device.label() }, String { }, constraints);
118 break;
119 case CaptureDevice::DeviceType::Microphone:
120 case CaptureDevice::DeviceType::Camera:
121 case CaptureDevice::DeviceType::Unknown:
122 ASSERT_NOT_REACHED();
123 break;
124 }
125
126 return { };
127 }
128private:
129 CaptureDeviceManager& displayCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().displayCaptureDeviceManager(); }
130};
131
132class MockRealtimeAudioSourceFactory : public AudioCaptureFactory {
133public:
134 CaptureSourceOrError createAudioCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
135 {
136 ASSERT(device.type() == CaptureDevice::DeviceType::Microphone);
137 ASSERT(MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, device.persistentId()));
138
139 return MockRealtimeAudioSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
140 }
141private:
142 CaptureDeviceManager& audioCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().audioCaptureDeviceManager(); }
143};
144
145static Vector<MockMediaDevice>& devices()
146{
147 static auto devices = makeNeverDestroyed([] {
148 return defaultDevices();
149 }());
150 return devices;
151}
152
153static HashMap<String, MockMediaDevice>& deviceMap()
154{
155 static auto map = makeNeverDestroyed([] {
156 HashMap<String, MockMediaDevice> map;
157 for (auto& device : devices())
158 map.add(device.persistentId, device);
159
160 return map;
161 }());
162 return map;
163}
164
165static inline Vector<CaptureDevice>& deviceListForDevice(const MockMediaDevice& device)
166{
167 if (device.isMicrophone())
168 return MockRealtimeMediaSourceCenter::audioDevices();
169 if (device.isCamera())
170 return MockRealtimeMediaSourceCenter::videoDevices();
171
172 ASSERT(device.isDisplay());
173 return MockRealtimeMediaSourceCenter::displayDevices();
174}
175
176MockRealtimeMediaSourceCenter& MockRealtimeMediaSourceCenter::singleton()
177{
178 static NeverDestroyed<MockRealtimeMediaSourceCenter> center;
179 return center;
180}
181
182void MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(bool enabled)
183{
184 static bool active = false;
185 if (active == enabled)
186 return;
187
188 active = enabled;
189
190 RealtimeMediaSourceCenter& center = RealtimeMediaSourceCenter::singleton();
191 MockRealtimeMediaSourceCenter& mock = singleton();
192
193 if (active) {
194 if (mock.m_isMockAudioCaptureEnabled)
195 center.setAudioCaptureFactory(mock.audioCaptureFactory());
196 if (mock.m_isMockVideoCaptureEnabled)
197 center.setVideoCaptureFactory(mock.videoCaptureFactory());
198 if (mock.m_isMockDisplayCaptureEnabled)
199 center.setDisplayCaptureFactory(mock.displayCaptureFactory());
200 return;
201 }
202
203 if (mock.m_isMockAudioCaptureEnabled)
204 center.unsetAudioCaptureFactory(mock.audioCaptureFactory());
205 if (mock.m_isMockVideoCaptureEnabled)
206 center.unsetVideoCaptureFactory(mock.videoCaptureFactory());
207 if (mock.m_isMockDisplayCaptureEnabled)
208 center.unsetDisplayCaptureFactory(mock.displayCaptureFactory());
209}
210
211bool MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled()
212{
213 MockRealtimeMediaSourceCenter& mock = singleton();
214 RealtimeMediaSourceCenter& center = RealtimeMediaSourceCenter::singleton();
215
216 return &center.audioCaptureFactory() == &mock.audioCaptureFactory() || &center.videoCaptureFactory() == &mock.videoCaptureFactory() || &center.displayCaptureFactory() == &mock.displayCaptureFactory();
217}
218
219static void createCaptureDevice(const MockMediaDevice& device)
220{
221 deviceListForDevice(device).append(MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(device.type(), device.persistentId).value());
222}
223
224void MockRealtimeMediaSourceCenter::resetDevices()
225{
226 setDevices(defaultDevices());
227 RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
228}
229
230void MockRealtimeMediaSourceCenter::setDevices(Vector<MockMediaDevice>&& newMockDevices)
231{
232 audioDevices().clear();
233 videoDevices().clear();
234 displayDevices().clear();
235
236 auto& mockDevices = devices();
237 mockDevices = WTFMove(newMockDevices);
238
239 auto& map = deviceMap();
240 map.clear();
241
242 for (const auto& device : mockDevices) {
243 map.add(device.persistentId, device);
244 createCaptureDevice(device);
245 }
246 RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
247}
248
249void MockRealtimeMediaSourceCenter::addDevice(const MockMediaDevice& device)
250{
251 devices().append(device);
252 deviceMap().set(device.persistentId, device);
253 createCaptureDevice(device);
254 RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
255}
256
257void MockRealtimeMediaSourceCenter::removeDevice(const String& persistentId)
258{
259 auto& map = deviceMap();
260 auto iterator = map.find(persistentId);
261 if (iterator == map.end())
262 return;
263
264 devices().removeFirstMatching([&persistentId](const auto& device) {
265 return device.persistentId == persistentId;
266 });
267
268 deviceListForDevice(iterator->value).removeFirstMatching([&persistentId](const auto& device) {
269 return device.persistentId() == persistentId;
270 });
271
272 map.remove(iterator);
273 RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
274}
275
276Optional<MockMediaDevice> MockRealtimeMediaSourceCenter::mockDeviceWithPersistentID(const String& id)
277{
278 ASSERT(!id.isEmpty());
279
280 auto& map = deviceMap();
281 auto iterator = map.find(id);
282 if (iterator == map.end())
283 return WTF::nullopt;
284
285 return iterator->value;
286}
287
288Optional<CaptureDevice> MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
289{
290 ASSERT(!id.isEmpty());
291
292 auto& map = deviceMap();
293 auto iterator = map.find(id);
294 if (iterator == map.end() || iterator->value.type() != type)
295 return WTF::nullopt;
296
297 CaptureDevice device { iterator->value.persistentId, type, iterator->value.label };
298 device.setEnabled(true);
299 return device;
300}
301
302Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::audioDevices()
303{
304 static auto audioDevices = makeNeverDestroyed([] {
305 Vector<CaptureDevice> audioDevices;
306 for (const auto& device : devices()) {
307 if (device.isMicrophone())
308 audioDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, device.persistentId).value());
309 }
310 return audioDevices;
311 }());
312
313 return audioDevices;
314}
315
316Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::videoDevices()
317{
318 static auto videoDevices = makeNeverDestroyed([] {
319 Vector<CaptureDevice> videoDevices;
320 for (const auto& device : devices()) {
321 if (device.isCamera())
322 videoDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, device.persistentId).value());
323 }
324 return videoDevices;
325 }());
326
327 return videoDevices;
328}
329
330Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::displayDevices()
331{
332 static auto displayDevices = makeNeverDestroyed([] {
333 Vector<CaptureDevice> displayDevices;
334 for (const auto& device : devices()) {
335 if (device.isDisplay())
336 displayDevices.append(captureDeviceWithPersistentID(CaptureDevice::DeviceType::Screen, device.persistentId).value());
337 }
338 return displayDevices;
339 }());
340
341 return displayDevices;
342}
343
344AudioCaptureFactory& MockRealtimeMediaSourceCenter::audioCaptureFactory()
345{
346 static NeverDestroyed<MockRealtimeAudioSourceFactory> factory;
347 return factory.get();
348}
349
350VideoCaptureFactory& MockRealtimeMediaSourceCenter::videoCaptureFactory()
351{
352 static NeverDestroyed<MockRealtimeVideoSourceFactory> factory;
353 return factory.get();
354}
355
356DisplayCaptureFactory& MockRealtimeMediaSourceCenter::displayCaptureFactory()
357{
358 static NeverDestroyed<MockRealtimeDisplaySourceFactory> factory;
359 return factory.get();
360}
361
362} // namespace WebCore
363
364#endif // ENABLE(MEDIA_STREAM)
365