1/*
2 * Copyright (C) 2011 Ericsson AB. All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
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 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * 3. Neither the name of Ericsson nor the names of its contributors
17 * may be used to endorse or promote products derived from this
18 * software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "config.h"
34#include "RealtimeMediaSourceCenter.h"
35
36#if ENABLE(MEDIA_STREAM)
37
38#include "CaptureDeviceManager.h"
39#include "Logging.h"
40#include "MediaStreamPrivate.h"
41#include <wtf/SHA1.h>
42
43namespace WebCore {
44
45RealtimeMediaSourceCenter& RealtimeMediaSourceCenter::singleton()
46{
47 ASSERT(isMainThread());
48 static NeverDestroyed<RealtimeMediaSourceCenter> center;
49 return center;
50}
51
52RealtimeMediaSourceCenter::RealtimeMediaSourceCenter()
53{
54 m_supportedConstraints.setSupportsWidth(true);
55 m_supportedConstraints.setSupportsHeight(true);
56 m_supportedConstraints.setSupportsAspectRatio(true);
57 m_supportedConstraints.setSupportsFrameRate(true);
58 m_supportedConstraints.setSupportsFacingMode(true);
59 m_supportedConstraints.setSupportsVolume(true);
60 m_supportedConstraints.setSupportsDeviceId(true);
61}
62
63RealtimeMediaSourceCenter::~RealtimeMediaSourceCenter() = default;
64
65void RealtimeMediaSourceCenter::createMediaStream(NewMediaStreamHandler&& completionHandler, String&& hashSalt, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, const MediaStreamRequest& request)
66{
67 Vector<Ref<RealtimeMediaSource>> audioSources;
68 Vector<Ref<RealtimeMediaSource>> videoSources;
69 String invalidConstraint;
70
71 if (audioDevice) {
72 auto audioSource = audioCaptureFactory().createAudioCaptureSource(WTFMove(audioDevice), String { hashSalt }, &request.audioConstraints);
73 if (audioSource)
74 audioSources.append(audioSource.source());
75 else {
76#if !LOG_DISABLED
77 if (!audioSource.errorMessage.isEmpty())
78 LOG(Media, "RealtimeMediaSourceCenter::createMediaStream(%p), audio constraints failed to apply: %s", this, audioSource.errorMessage.utf8().data());
79#endif
80 completionHandler(nullptr);
81 return;
82 }
83 }
84
85 if (videoDevice) {
86 CaptureSourceOrError videoSource;
87 if (videoDevice.type() == CaptureDevice::DeviceType::Camera)
88 videoSource = videoCaptureFactory().createVideoCaptureSource(WTFMove(videoDevice), WTFMove(hashSalt), &request.videoConstraints);
89 else
90 videoSource = displayCaptureFactory().createDisplayCaptureSource(WTFMove(videoDevice), &request.videoConstraints);
91
92 if (videoSource)
93 videoSources.append(videoSource.source());
94 else {
95#if !LOG_DISABLED
96 if (!videoSource.errorMessage.isEmpty())
97 LOG(Media, "RealtimeMediaSourceCenter::createMediaStream(%p), video constraints failed to apply: %s", this, videoSource.errorMessage.utf8().data());
98#endif
99 completionHandler(nullptr);
100 return;
101 }
102 }
103
104 completionHandler(MediaStreamPrivate::create(audioSources, videoSources));
105}
106
107Vector<CaptureDevice> RealtimeMediaSourceCenter::getMediaStreamDevices()
108{
109 Vector<CaptureDevice> result;
110 for (auto& device : audioCaptureFactory().audioCaptureDeviceManager().captureDevices()) {
111 if (device.enabled())
112 result.append(device);
113 }
114 for (auto& device : videoCaptureFactory().videoCaptureDeviceManager().captureDevices()) {
115 if (device.enabled())
116 result.append(device);
117 }
118 for (auto& device : displayCaptureFactory().displayCaptureDeviceManager().captureDevices()) {
119 if (device.enabled())
120 result.append(device);
121 }
122
123 return result;
124}
125
126static void addStringToSHA1(SHA1& sha1, const String& string)
127{
128 if (string.isEmpty())
129 return;
130
131 if (string.is8Bit() && string.isAllASCII()) {
132 const uint8_t nullByte = 0;
133 sha1.addBytes(string.characters8(), string.length());
134 sha1.addBytes(&nullByte, 1);
135 return;
136 }
137
138 auto utf8 = string.utf8();
139 sha1.addBytes(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length() + 1); // Include terminating null byte.
140}
141
142String RealtimeMediaSourceCenter::hashStringWithSalt(const String& id, const String& hashSalt)
143{
144 if (id.isEmpty() || hashSalt.isEmpty())
145 return emptyString();
146
147 SHA1 sha1;
148
149 addStringToSHA1(sha1, id);
150 addStringToSHA1(sha1, hashSalt);
151
152 SHA1::Digest digest;
153 sha1.computeHash(digest);
154
155 return SHA1::hexDigest(digest).data();
156}
157
158void RealtimeMediaSourceCenter::setDevicesChangedObserver(std::function<void()>&& observer)
159{
160 ASSERT(isMainThread());
161 ASSERT(!m_deviceChangedObserver);
162 m_deviceChangedObserver = WTFMove(observer);
163}
164
165void RealtimeMediaSourceCenter::captureDevicesChanged()
166{
167 ASSERT(isMainThread());
168 if (m_deviceChangedObserver)
169 m_deviceChangedObserver();
170}
171
172void RealtimeMediaSourceCenter::getDisplayMediaDevices(const MediaStreamRequest& request, Vector<DeviceInfo>& diaplayDeviceInfo, String& firstInvalidConstraint)
173{
174 if (!request.videoConstraints.isValid)
175 return;
176
177 String invalidConstraint;
178 for (auto& device : displayCaptureFactory().displayCaptureDeviceManager().captureDevices()) {
179 if (!device.enabled())
180 return;
181
182 auto sourceOrError = displayCaptureFactory().createDisplayCaptureSource(device, { });
183 if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.videoConstraints, invalidConstraint))
184 diaplayDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
185
186 if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
187 firstInvalidConstraint = invalidConstraint;
188 }
189}
190
191void RealtimeMediaSourceCenter::getUserMediaDevices(const MediaStreamRequest& request, String&& hashSalt, Vector<DeviceInfo>& audioDeviceInfo, Vector<DeviceInfo>& videoDeviceInfo, String& firstInvalidConstraint)
192{
193 String invalidConstraint;
194 if (request.audioConstraints.isValid) {
195 for (auto& device : audioCaptureFactory().audioCaptureDeviceManager().captureDevices()) {
196 if (!device.enabled())
197 continue;
198
199 auto sourceOrError = audioCaptureFactory().createAudioCaptureSource(device, String { hashSalt }, { });
200 if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.audioConstraints, invalidConstraint))
201 audioDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
202
203 if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
204 firstInvalidConstraint = invalidConstraint;
205 }
206 }
207
208 if (request.videoConstraints.isValid) {
209 for (auto& device : videoCaptureFactory().videoCaptureDeviceManager().captureDevices()) {
210 if (!device.enabled())
211 continue;
212
213 auto sourceOrError = videoCaptureFactory().createVideoCaptureSource(device, String { hashSalt }, { });
214 if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.videoConstraints, invalidConstraint))
215 videoDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
216
217 if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
218 firstInvalidConstraint = invalidConstraint;
219 }
220 }
221}
222
223void RealtimeMediaSourceCenter::validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaStreamRequest& request, String&& deviceIdentifierHashSalt)
224{
225 struct {
226 bool operator()(const DeviceInfo& a, const DeviceInfo& b)
227 {
228 return a.fitnessScore > b.fitnessScore;
229 }
230 } sortBasedOnFitnessScore;
231
232 Vector<DeviceInfo> audioDeviceInfo;
233 Vector<DeviceInfo> videoDeviceInfo;
234 String firstInvalidConstraint;
235
236 if (request.type == MediaStreamRequest::Type::DisplayMedia)
237 getDisplayMediaDevices(request, videoDeviceInfo, firstInvalidConstraint);
238 else
239 getUserMediaDevices(request, String { deviceIdentifierHashSalt }, audioDeviceInfo, videoDeviceInfo, firstInvalidConstraint);
240
241 if ((request.audioConstraints.isValid && audioDeviceInfo.isEmpty()) || (request.videoConstraints.isValid && videoDeviceInfo.isEmpty())) {
242 invalidHandler(firstInvalidConstraint);
243 return;
244 }
245
246 Vector<CaptureDevice> audioDevices;
247 if (!audioDeviceInfo.isEmpty()) {
248 std::sort(audioDeviceInfo.begin(), audioDeviceInfo.end(), sortBasedOnFitnessScore);
249 audioDevices = WTF::map(audioDeviceInfo, [] (auto& info) {
250 return info.device;
251 });
252 }
253
254 Vector<CaptureDevice> videoDevices;
255 if (!videoDeviceInfo.isEmpty()) {
256 std::sort(videoDeviceInfo.begin(), videoDeviceInfo.end(), sortBasedOnFitnessScore);
257 videoDevices = WTF::map(videoDeviceInfo, [] (auto& info) {
258 return info.device;
259 });
260 }
261
262 validHandler(WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(deviceIdentifierHashSalt));
263}
264
265void RealtimeMediaSourceCenter::setVideoCapturePageState(bool interrupted, bool pageMuted)
266{
267 videoCaptureFactory().setVideoCapturePageState(interrupted, pageMuted);
268}
269
270void RealtimeMediaSourceCenter::setAudioCaptureFactory(AudioCaptureFactory& factory)
271{
272 m_audioCaptureFactoryOverride = &factory;
273}
274
275void RealtimeMediaSourceCenter::unsetAudioCaptureFactory(AudioCaptureFactory& oldOverride)
276{
277 ASSERT_UNUSED(oldOverride, m_audioCaptureFactoryOverride == &oldOverride);
278 if (&oldOverride == m_audioCaptureFactoryOverride)
279 m_audioCaptureFactoryOverride = nullptr;
280}
281
282AudioCaptureFactory& RealtimeMediaSourceCenter::audioCaptureFactory()
283{
284 return m_audioCaptureFactoryOverride ? *m_audioCaptureFactoryOverride : defaultAudioCaptureFactory();
285}
286
287void RealtimeMediaSourceCenter::setVideoCaptureFactory(VideoCaptureFactory& factory)
288{
289 m_videoCaptureFactoryOverride = &factory;
290}
291void RealtimeMediaSourceCenter::unsetVideoCaptureFactory(VideoCaptureFactory& oldOverride)
292{
293 ASSERT_UNUSED(oldOverride, m_videoCaptureFactoryOverride == &oldOverride);
294 if (&oldOverride == m_videoCaptureFactoryOverride)
295 m_videoCaptureFactoryOverride = nullptr;
296}
297
298VideoCaptureFactory& RealtimeMediaSourceCenter::videoCaptureFactory()
299{
300 return m_videoCaptureFactoryOverride ? *m_videoCaptureFactoryOverride : defaultVideoCaptureFactory();
301}
302
303void RealtimeMediaSourceCenter::setDisplayCaptureFactory(DisplayCaptureFactory& factory)
304{
305 m_displayCaptureFactoryOverride = &factory;
306}
307
308void RealtimeMediaSourceCenter::unsetDisplayCaptureFactory(DisplayCaptureFactory& oldOverride)
309{
310 ASSERT_UNUSED(oldOverride, m_displayCaptureFactoryOverride == &oldOverride);
311 if (&oldOverride == m_displayCaptureFactoryOverride)
312 m_displayCaptureFactoryOverride = nullptr;
313}
314
315DisplayCaptureFactory& RealtimeMediaSourceCenter::displayCaptureFactory()
316{
317 return m_displayCaptureFactoryOverride ? *m_displayCaptureFactoryOverride : defaultDisplayCaptureFactory();
318}
319
320} // namespace WebCore
321
322#endif // ENABLE(MEDIA_STREAM)
323