Unreviewed, rolling out r244627.
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / AVCaptureDeviceManager.mm
1 /*
2  * Copyright (C) 2013-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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "AVCaptureDeviceManager.h"
28
29 #if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
30
31 #import "AVVideoCaptureSource.h"
32 #import "AudioSourceProvider.h"
33 #import "Logging.h"
34 #import "MediaConstraints.h"
35 #import "RealtimeMediaSource.h"
36 #import "RealtimeMediaSourceCenter.h"
37 #import "RealtimeMediaSourceSettings.h"
38 #import "RealtimeMediaSourceSupportedConstraints.h"
39 #import <AVFoundation/AVCaptureDevice.h>
40 #import <AVFoundation/AVCaptureSession.h>
41 #import <objc/runtime.h>
42 #import <wtf/MainThread.h>
43 #import <wtf/NeverDestroyed.h>
44 #import <wtf/SoftLinking.h>
45
46 typedef AVCaptureDevice AVCaptureDeviceTypedef;
47 typedef AVCaptureSession AVCaptureSessionType;
48
49 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
50
51 SOFT_LINK_CLASS(AVFoundation, AVCaptureDevice)
52 SOFT_LINK_CLASS(AVFoundation, AVCaptureSession)
53
54 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeAudio, NSString *)
55 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeMuxed, NSString *)
56 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeVideo, NSString *)
57 SOFT_LINK_CONSTANT(AVFoundation, AVCaptureDeviceWasConnectedNotification, NSString *)
58 SOFT_LINK_CONSTANT(AVFoundation, AVCaptureDeviceWasDisconnectedNotification, NSString *)
59
60 #define AVMediaTypeAudio getAVMediaTypeAudio()
61 #define AVMediaTypeMuxed getAVMediaTypeMuxed()
62 #define AVMediaTypeVideo getAVMediaTypeVideo()
63 #define AVCaptureDeviceWasConnectedNotification getAVCaptureDeviceWasConnectedNotification()
64 #define AVCaptureDeviceWasDisconnectedNotification getAVCaptureDeviceWasDisconnectedNotification()
65
66 using namespace WebCore;
67
68 @interface WebCoreAVCaptureDeviceManagerObserver : NSObject
69 {
70     AVCaptureDeviceManager* m_callback;
71 }
72
73 -(id)initWithCallback:(AVCaptureDeviceManager*)callback;
74 -(void)disconnect;
75 -(void)deviceConnectedDidChange:(NSNotification *)notification;
76 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context;
77 @end
78
79 namespace WebCore {
80
81
82 Vector<CaptureDevice>& AVCaptureDeviceManager::captureDevicesInternal()
83 {
84     if (!isAvailable())
85         return m_devices;
86
87     static bool firstTime = true;
88     if (firstTime) {
89         firstTime = false;
90         refreshCaptureDevices();
91         m_notifyWhenDeviceListChanges = true;
92     }
93
94     return m_devices;
95 }
96
97 const Vector<CaptureDevice>& AVCaptureDeviceManager::captureDevices()
98 {
99     return captureDevicesInternal();
100 }
101
102 inline static bool deviceIsAvailable(AVCaptureDeviceTypedef *device)
103 {
104     if (![device isConnected])
105         return false;
106
107 #if !PLATFORM(IOS_FAMILY)
108     if ([device isSuspended])
109         return false;
110 #endif
111
112     return true;
113 }
114
115 void AVCaptureDeviceManager::updateCachedAVCaptureDevices()
116 {
117     auto* currentDevices = [getAVCaptureDeviceClass() devices];
118     auto changedDevices = adoptNS([[NSMutableArray alloc] init]);
119     for (AVCaptureDeviceTypedef *cachedDevice in m_avCaptureDevices.get()) {
120         if (![currentDevices containsObject:cachedDevice])
121             [changedDevices addObject:cachedDevice];
122     }
123
124     if ([changedDevices count]) {
125         for (AVCaptureDeviceTypedef *device in changedDevices.get())
126             [device removeObserver:m_objcObserver.get() forKeyPath:@"suspended"];
127         [m_avCaptureDevices removeObjectsInArray:changedDevices.get()];
128     }
129
130     for (AVCaptureDeviceTypedef *device in currentDevices) {
131
132         if (![device hasMediaType:AVMediaTypeVideo] && ![device hasMediaType:AVMediaTypeMuxed])
133             continue;
134
135         if ([m_avCaptureDevices.get() containsObject:device])
136             continue;
137
138         [device addObserver:m_objcObserver.get() forKeyPath:@"suspended" options:NSKeyValueObservingOptionNew context:(void *)nil];
139         [m_avCaptureDevices.get() addObject:device];
140     }
141
142 }
143
144 void AVCaptureDeviceManager::refreshCaptureDevices()
145 {
146     if (!m_avCaptureDevices) {
147         m_avCaptureDevices = adoptNS([[NSMutableArray alloc] init]);
148         registerForDeviceNotifications();
149     }
150
151     updateCachedAVCaptureDevices();
152
153     bool deviceHasChanged = false;
154     auto* currentDevices = [getAVCaptureDeviceClass() devices];
155     Vector<CaptureDevice> deviceList;
156     for (AVCaptureDeviceTypedef *platformDevice in currentDevices) {
157
158         if (![platformDevice hasMediaType:AVMediaTypeVideo] && ![platformDevice hasMediaType:AVMediaTypeMuxed])
159             continue;
160
161         CaptureDevice captureDevice(platformDevice.uniqueID, CaptureDevice::DeviceType::Camera, platformDevice.localizedName);
162         captureDevice.setEnabled(deviceIsAvailable(platformDevice));
163
164         CaptureDevice existingCaptureDevice = captureDeviceFromPersistentID(platformDevice.uniqueID);
165         if (!existingCaptureDevice || (existingCaptureDevice && existingCaptureDevice.type() == CaptureDevice::DeviceType::Camera && captureDevice.enabled() != existingCaptureDevice.enabled()))
166             deviceHasChanged = true;
167
168         deviceList.append(WTFMove(captureDevice));
169     }
170
171     if (deviceHasChanged || m_devices.size() != deviceList.size()) {
172         deviceHasChanged = true;
173         m_devices = WTFMove(deviceList);
174     }
175
176     if (m_notifyWhenDeviceListChanges && deviceHasChanged)
177         deviceChanged();
178 }
179
180 bool AVCaptureDeviceManager::isAvailable()
181 {
182     return AVFoundationLibrary();
183 }
184
185 AVCaptureDeviceManager& AVCaptureDeviceManager::singleton()
186 {
187     static NeverDestroyed<AVCaptureDeviceManager> manager;
188     return manager;
189 }
190
191 AVCaptureDeviceManager::AVCaptureDeviceManager()
192     : m_objcObserver(adoptNS([[WebCoreAVCaptureDeviceManagerObserver alloc] initWithCallback: this]))
193 {
194 }
195
196 AVCaptureDeviceManager::~AVCaptureDeviceManager()
197 {
198     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
199     [m_objcObserver disconnect];
200     for (AVCaptureDeviceTypedef *device in m_avCaptureDevices.get())
201         [device removeObserver:m_objcObserver.get() forKeyPath:@"suspended"];
202 }
203
204 void AVCaptureDeviceManager::registerForDeviceNotifications()
205 {
206     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(deviceConnectedDidChange:) name:AVCaptureDeviceWasConnectedNotification object:nil];
207     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(deviceConnectedDidChange:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
208 }
209
210 } // namespace WebCore
211
212 @implementation WebCoreAVCaptureDeviceManagerObserver
213
214 - (id)initWithCallback:(AVCaptureDeviceManager*)callback
215 {
216     self = [super init];
217     if (!self)
218         return nil;
219     m_callback = callback;
220     return self;
221 }
222
223 - (void)disconnect
224 {
225     [NSObject cancelPreviousPerformRequestsWithTarget:self];
226     m_callback = nil;
227 }
228
229 - (void)deviceConnectedDidChange:(NSNotification *)unusedNotification
230 {
231     UNUSED_PARAM(unusedNotification);
232     if (!m_callback)
233         return;
234
235     dispatch_async(dispatch_get_main_queue(), ^{
236         if (m_callback)
237             m_callback->refreshCaptureDevices();
238     });
239 }
240
241 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
242 {
243     UNUSED_PARAM(object);
244     UNUSED_PARAM(context);
245     UNUSED_PARAM(change);
246
247     if (!m_callback)
248         return;
249
250     if ([keyPath isEqualToString:@"suspended"])
251         m_callback->refreshCaptureDevices();
252 }
253
254 @end
255
256 #endif // ENABLE(MEDIA_STREAM)