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 | |
44 | namespace WebCore { |
45 | |
46 | static 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 | |
87 | class MockRealtimeVideoSourceFactory : public VideoCaptureFactory { |
88 | public: |
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 | |
97 | private: |
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 | |
108 | class MockRealtimeDisplaySourceFactory : public DisplayCaptureFactory { |
109 | public: |
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 | } |
128 | private: |
129 | CaptureDeviceManager& displayCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().displayCaptureDeviceManager(); } |
130 | }; |
131 | |
132 | class MockRealtimeAudioSourceFactory : public AudioCaptureFactory { |
133 | public: |
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 | } |
141 | private: |
142 | CaptureDeviceManager& audioCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().audioCaptureDeviceManager(); } |
143 | }; |
144 | |
145 | static Vector<MockMediaDevice>& devices() |
146 | { |
147 | static auto devices = makeNeverDestroyed([] { |
148 | return defaultDevices(); |
149 | }()); |
150 | return devices; |
151 | } |
152 | |
153 | static 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 | |
165 | static 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 | |
176 | MockRealtimeMediaSourceCenter& MockRealtimeMediaSourceCenter::singleton() |
177 | { |
178 | static NeverDestroyed<MockRealtimeMediaSourceCenter> center; |
179 | return center; |
180 | } |
181 | |
182 | void 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 | |
211 | bool MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled() |
212 | { |
213 | MockRealtimeMediaSourceCenter& mock = singleton(); |
214 | RealtimeMediaSourceCenter& center = RealtimeMediaSourceCenter::singleton(); |
215 | |
216 | return ¢er.audioCaptureFactory() == &mock.audioCaptureFactory() || ¢er.videoCaptureFactory() == &mock.videoCaptureFactory() || ¢er.displayCaptureFactory() == &mock.displayCaptureFactory(); |
217 | } |
218 | |
219 | static void createCaptureDevice(const MockMediaDevice& device) |
220 | { |
221 | deviceListForDevice(device).append(MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(device.type(), device.persistentId).value()); |
222 | } |
223 | |
224 | void MockRealtimeMediaSourceCenter::resetDevices() |
225 | { |
226 | setDevices(defaultDevices()); |
227 | RealtimeMediaSourceCenter::singleton().captureDevicesChanged(); |
228 | } |
229 | |
230 | void 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 | |
249 | void 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 | |
257 | void 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 | |
276 | Optional<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 | |
288 | Optional<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 | |
302 | Vector<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 | |
316 | Vector<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 | |
330 | Vector<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 | |
344 | AudioCaptureFactory& MockRealtimeMediaSourceCenter::audioCaptureFactory() |
345 | { |
346 | static NeverDestroyed<MockRealtimeAudioSourceFactory> factory; |
347 | return factory.get(); |
348 | } |
349 | |
350 | VideoCaptureFactory& MockRealtimeMediaSourceCenter::videoCaptureFactory() |
351 | { |
352 | static NeverDestroyed<MockRealtimeVideoSourceFactory> factory; |
353 | return factory.get(); |
354 | } |
355 | |
356 | DisplayCaptureFactory& MockRealtimeMediaSourceCenter::displayCaptureFactory() |
357 | { |
358 | static NeverDestroyed<MockRealtimeDisplaySourceFactory> factory; |
359 | return factory.get(); |
360 | } |
361 | |
362 | } // namespace WebCore |
363 | |
364 | #endif // ENABLE(MEDIA_STREAM) |
365 | |