[MediaStream] RealtimeMediaSource should be able to vend hashed IDs
[WebKit-https.git] / Source / WebCore / platform / mediastream / RealtimeMediaSourceCenter.cpp
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 // FIXME: GTK to implement its own RealtimeMediaSourceCenter.
39 #if PLATFORM(GTK)
40 #include "MockRealtimeMediaSourceCenter.h"
41 #endif
42
43 #include "CaptureDeviceManager.h"
44 #include "Logging.h"
45 #include "MediaStreamPrivate.h"
46 #include <wtf/SHA1.h>
47
48 namespace WebCore {
49
50 static RealtimeMediaSourceCenter*& mediaStreamCenterOverride()
51 {
52     static RealtimeMediaSourceCenter* override;
53     return override;
54 }
55
56 RealtimeMediaSourceCenter& RealtimeMediaSourceCenter::singleton()
57 {
58     RealtimeMediaSourceCenter* override = mediaStreamCenterOverride();
59     if (override)
60         return *override;
61     return RealtimeMediaSourceCenter::platformCenter();
62 }
63
64 void RealtimeMediaSourceCenter::setSharedStreamCenterOverride(RealtimeMediaSourceCenter* center)
65 {
66     mediaStreamCenterOverride() = center;
67 }
68
69 RealtimeMediaSourceCenter::RealtimeMediaSourceCenter()
70 {
71     m_supportedConstraints.setSupportsWidth(true);
72     m_supportedConstraints.setSupportsHeight(true);
73     m_supportedConstraints.setSupportsAspectRatio(true);
74     m_supportedConstraints.setSupportsFrameRate(true);
75     m_supportedConstraints.setSupportsFacingMode(true);
76     m_supportedConstraints.setSupportsVolume(true);
77     m_supportedConstraints.setSupportsDeviceId(true);
78 }
79
80 RealtimeMediaSourceCenter::~RealtimeMediaSourceCenter() = default;
81
82 void RealtimeMediaSourceCenter::createMediaStream(NewMediaStreamHandler&& completionHandler, String&& hashSalt, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, const MediaStreamRequest& request)
83 {
84     Vector<Ref<RealtimeMediaSource>> audioSources;
85     Vector<Ref<RealtimeMediaSource>> videoSources;
86     String invalidConstraint;
87
88     if (audioDevice) {
89         auto audioSource = audioFactory().createAudioCaptureSource(WTFMove(audioDevice), String { hashSalt }, &request.audioConstraints);
90         if (audioSource)
91             audioSources.append(audioSource.source());
92         else {
93 #if !LOG_DISABLED
94             if (!audioSource.errorMessage.isEmpty())
95                 LOG(Media, "RealtimeMediaSourceCenter::createMediaStream(%p), audio constraints failed to apply: %s", this, audioSource.errorMessage.utf8().data());
96 #endif
97             completionHandler(nullptr);
98             return;
99         }
100     }
101
102     if (videoDevice) {
103         CaptureSourceOrError videoSource;
104         if (videoDevice.type() == CaptureDevice::DeviceType::Camera)
105             videoSource = videoFactory().createVideoCaptureSource(WTFMove(videoDevice), WTFMove(hashSalt), &request.videoConstraints);
106         else
107             videoSource = displayCaptureFactory().createDisplayCaptureSource(WTFMove(videoDevice), &request.videoConstraints);
108
109         if (videoSource)
110             videoSources.append(videoSource.source());
111         else {
112 #if !LOG_DISABLED
113             if (!videoSource.errorMessage.isEmpty())
114                 LOG(Media, "RealtimeMediaSourceCenter::createMediaStream(%p), video constraints failed to apply: %s", this, videoSource.errorMessage.utf8().data());
115 #endif
116             completionHandler(nullptr);
117             return;
118         }
119     }
120
121     completionHandler(MediaStreamPrivate::create(audioSources, videoSources));
122 }
123
124 Vector<CaptureDevice> RealtimeMediaSourceCenter::getMediaStreamDevices()
125 {
126     Vector<CaptureDevice> result;
127     for (auto& device : audioCaptureDeviceManager().captureDevices()) {
128         if (device.enabled())
129             result.append(device);
130     }
131     for (auto& device : videoCaptureDeviceManager().captureDevices()) {
132         if (device.enabled())
133             result.append(device);
134     }
135     for (auto& device : displayCaptureDeviceManager().captureDevices()) {
136         if (device.enabled())
137             result.append(device);
138     }
139
140     return result;
141 }
142
143 static void addStringToSHA1(SHA1& sha1, const String& string)
144 {
145     if (string.isEmpty())
146         return;
147
148     if (string.is8Bit() && string.isAllASCII()) {
149         const uint8_t nullByte = 0;
150         sha1.addBytes(string.characters8(), string.length());
151         sha1.addBytes(&nullByte, 1);
152         return;
153     }
154
155     auto utf8 = string.utf8();
156     sha1.addBytes(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length() + 1); // Include terminating null byte.
157 }
158
159 String RealtimeMediaSourceCenter::hashStringWithSalt(const String& id, const String& hashSalt)
160 {
161     if (id.isEmpty() || hashSalt.isEmpty())
162         return emptyString();
163
164     SHA1 sha1;
165
166     addStringToSHA1(sha1, id);
167     addStringToSHA1(sha1, hashSalt);
168     
169     SHA1::Digest digest;
170     sha1.computeHash(digest);
171     
172     return SHA1::hexDigest(digest).data();
173 }
174
175 CaptureDevice RealtimeMediaSourceCenter::captureDeviceWithUniqueID(const String& uniqueID, const String& idHashSalt)
176 {
177     for (auto& device : getMediaStreamDevices()) {
178         if (uniqueID == hashStringWithSalt(device.persistentId(), idHashSalt))
179             return device;
180     }
181
182     return { };
183 }
184
185 ExceptionOr<void> RealtimeMediaSourceCenter::setDeviceEnabled(const String& id, bool enabled)
186 {
187     for (auto& captureDevice : getMediaStreamDevices()) {
188         if (id == captureDevice.persistentId()) {
189             if (enabled != captureDevice.enabled()) {
190                 captureDevice.setEnabled(enabled);
191                 captureDevicesChanged();
192             }
193
194             return { };
195         }
196     }
197
198     return Exception { NotFoundError };
199 }
200
201 void RealtimeMediaSourceCenter::setDevicesChangedObserver(std::function<void()>&& observer)
202 {
203     ASSERT(isMainThread());
204     ASSERT(!m_deviceChangedObserver);
205     m_deviceChangedObserver = WTFMove(observer);
206 }
207
208 void RealtimeMediaSourceCenter::captureDevicesChanged()
209 {
210     ASSERT(isMainThread());
211     if (m_deviceChangedObserver)
212         m_deviceChangedObserver();
213 }
214
215 void RealtimeMediaSourceCenter::getDisplayMediaDevices(const MediaStreamRequest& request, Vector<DeviceInfo>& diaplayDeviceInfo, String& firstInvalidConstraint)
216 {
217     if (!request.videoConstraints.isValid)
218         return;
219
220     String invalidConstraint;
221     for (auto& device : displayCaptureDeviceManager().captureDevices()) {
222         if (!device.enabled())
223             return;
224
225         auto sourceOrError = displayCaptureFactory().createDisplayCaptureSource(device, { });
226         if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.videoConstraints, invalidConstraint))
227             diaplayDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
228
229         if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
230             firstInvalidConstraint = invalidConstraint;
231     }
232 }
233
234 void RealtimeMediaSourceCenter::getUserMediaDevices(const MediaStreamRequest& request, String&& hashSalt, Vector<DeviceInfo>& audioDeviceInfo, Vector<DeviceInfo>& videoDeviceInfo, String& firstInvalidConstraint)
235 {
236     String invalidConstraint;
237     if (request.audioConstraints.isValid) {
238         for (auto& device : audioCaptureDeviceManager().captureDevices()) {
239             if (!device.enabled())
240                 continue;
241
242             auto sourceOrError = audioFactory().createAudioCaptureSource(device, String { hashSalt }, { });
243             if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.audioConstraints, invalidConstraint))
244                 audioDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
245
246             if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
247                 firstInvalidConstraint = invalidConstraint;
248         }
249     }
250
251     if (request.videoConstraints.isValid) {
252         for (auto& device : videoCaptureDeviceManager().captureDevices()) {
253             if (!device.enabled())
254                 continue;
255
256             auto sourceOrError = videoFactory().createVideoCaptureSource(device, String { hashSalt }, { });
257             if (sourceOrError && sourceOrError.captureSource->supportsConstraints(request.videoConstraints, invalidConstraint))
258                 videoDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device});
259
260             if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
261                 firstInvalidConstraint = invalidConstraint;
262         }
263     }
264 }
265
266 void RealtimeMediaSourceCenter::validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaStreamRequest& request, String&& deviceIdentifierHashSalt)
267 {
268     struct {
269         bool operator()(const DeviceInfo& a, const DeviceInfo& b)
270         {
271             return a.fitnessScore < b.fitnessScore;
272         }
273     } sortBasedOnFitnessScore;
274
275     Vector<DeviceInfo> audioDeviceInfo;
276     Vector<DeviceInfo> videoDeviceInfo;
277     String firstInvalidConstraint;
278
279     if (request.type == MediaStreamRequest::Type::DisplayMedia)
280         getDisplayMediaDevices(request, videoDeviceInfo, firstInvalidConstraint);
281     else
282         getUserMediaDevices(request, String { deviceIdentifierHashSalt }, audioDeviceInfo, videoDeviceInfo, firstInvalidConstraint);
283
284     if ((request.audioConstraints.isValid && audioDeviceInfo.isEmpty()) || (request.videoConstraints.isValid && videoDeviceInfo.isEmpty())) {
285         invalidHandler(firstInvalidConstraint);
286         return;
287     }
288
289     Vector<CaptureDevice> audioDevices;
290     if (!audioDeviceInfo.isEmpty()) {
291         std::sort(audioDeviceInfo.begin(), audioDeviceInfo.end(), sortBasedOnFitnessScore);
292         audioDevices = WTF::map(audioDeviceInfo, [] (auto& info) {
293             return info.device;
294         });
295     }
296
297     Vector<CaptureDevice> videoDevices;
298     if (!videoDeviceInfo.isEmpty()) {
299         std::sort(videoDeviceInfo.begin(), videoDeviceInfo.end(), sortBasedOnFitnessScore);
300         videoDevices = WTF::map(videoDeviceInfo, [] (auto& info) {
301             return info.device;
302         });
303     }
304
305     validHandler(WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(deviceIdentifierHashSalt));
306 }
307
308 void RealtimeMediaSourceCenter::setVideoCapturePageState(bool interrupted, bool pageMuted)
309 {
310     videoFactory().setVideoCapturePageState(interrupted, pageMuted);
311 }
312
313 std::optional<CaptureDevice> RealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
314 {
315     switch (type) {
316     case CaptureDevice::DeviceType::Camera:
317         return videoCaptureDeviceManager().captureDeviceWithPersistentID(type, id);
318         break;
319     case CaptureDevice::DeviceType::Microphone:
320         return audioCaptureDeviceManager().captureDeviceWithPersistentID(type, id);
321         break;
322     case CaptureDevice::DeviceType::Screen:
323     case CaptureDevice::DeviceType::Application:
324     case CaptureDevice::DeviceType::Window:
325     case CaptureDevice::DeviceType::Browser:
326         return displayCaptureDeviceManager().captureDeviceWithPersistentID(type, id);
327         break;
328     case CaptureDevice::DeviceType::Unknown:
329         ASSERT_NOT_REACHED();
330         break;
331     }
332
333     return std::nullopt;
334 }
335
336 } // namespace WebCore
337
338 #endif // ENABLE(MEDIA_STREAM)