Add notifications from CaptureDeviceManager (and subclasses) when device lists change
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 May 2017 17:06:16 +0000 (17:06 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 May 2017 17:06:16 +0000 (17:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171442

Reviewed by Youenn Fablet.

Add a new callback-based notification to CaptureDeviceManager which fires when the underlying list
of devices changes. Add support for enumerating AVAudioSession capture devices.

* WebCore.xcodeproj/project.pbxproj:
* platform/mediastream/CaptureDeviceManager.cpp:
(nextObserverToken):
(CaptureDeviceManager::addCaptureDeviceChangedObserver):
(CaptureDeviceManager::removeCaptureDeviceChangedObserver):
* platform/mediastream/CaptureDeviceManager.h:
* platform/mediastream/ios/AVAudioSessionCaptureDevice.h: Added.
* platform/mediastream/ios/AVAudioSessionCaptureDevice.mm: Added.
(WebCore::AVAudioSessionCaptureDevice::create):
(WebCore::AVAudioSessionCaptureDevice::AVAudioSessionCaptureDevice):
* platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h: Added.
* platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm: Added.
(-[WebAVAudioSessionAvailableInputsListener initWithCallback:]):
(-[WebAVAudioSessionAvailableInputsListener invalidate]):
(-[WebAVAudioSessionAvailableInputsListener observeValueForKeyPath:ofObject:change:context:]):
(WebCore::AVAudioSessionCaptureDeviceManager::singleton):
(WebCore::AVAudioSessionCaptureDeviceManager::captureDevices):
(WebCore::AVAudioSessionCaptureDeviceManager::audioSessionCaptureDevices):
(WebCore::AVAudioSessionCaptureDeviceManager::audioSessionDeviceWithUID):
(WebCore::AVAudioSessionCaptureDeviceManager::refreshAudioCaptureDevices):
* platform/mediastream/mac/AVCaptureDeviceManager.mm:
(WebCore::AVCaptureDeviceManager::deviceConnected):
(WebCore::AVCaptureDeviceManager::deviceDisconnected):
* platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp:
(WebCore::CoreAudioCaptureDeviceManager::refreshAudioCaptureDevices):
(WebCore::CoreAudioCaptureDeviceManager::devicesChanged):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@216016 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/mediastream/CaptureDeviceManager.cpp
Source/WebCore/platform/mediastream/CaptureDeviceManager.h
Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.h [new file with mode: 0644]
Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.mm [new file with mode: 0644]
Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h [new file with mode: 0644]
Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm [new file with mode: 0644]
Source/WebCore/platform/mediastream/mac/AVCaptureDeviceManager.mm
Source/WebCore/platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp

index 55a9a24..7914a57 100644 (file)
@@ -1,3 +1,40 @@
+2017-05-01  Jer Noble  <jer.noble@apple.com>
+
+        Add notifications from CaptureDeviceManager (and subclasses) when device lists change
+        https://bugs.webkit.org/show_bug.cgi?id=171442
+
+        Reviewed by Youenn Fablet.
+
+        Add a new callback-based notification to CaptureDeviceManager which fires when the underlying list
+        of devices changes. Add support for enumerating AVAudioSession capture devices.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/mediastream/CaptureDeviceManager.cpp:
+        (nextObserverToken):
+        (CaptureDeviceManager::addCaptureDeviceChangedObserver):
+        (CaptureDeviceManager::removeCaptureDeviceChangedObserver):
+        * platform/mediastream/CaptureDeviceManager.h:
+        * platform/mediastream/ios/AVAudioSessionCaptureDevice.h: Added.
+        * platform/mediastream/ios/AVAudioSessionCaptureDevice.mm: Added.
+        (WebCore::AVAudioSessionCaptureDevice::create):
+        (WebCore::AVAudioSessionCaptureDevice::AVAudioSessionCaptureDevice):
+        * platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h: Added.
+        * platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm: Added.
+        (-[WebAVAudioSessionAvailableInputsListener initWithCallback:]):
+        (-[WebAVAudioSessionAvailableInputsListener invalidate]):
+        (-[WebAVAudioSessionAvailableInputsListener observeValueForKeyPath:ofObject:change:context:]):
+        (WebCore::AVAudioSessionCaptureDeviceManager::singleton):
+        (WebCore::AVAudioSessionCaptureDeviceManager::captureDevices):
+        (WebCore::AVAudioSessionCaptureDeviceManager::audioSessionCaptureDevices):
+        (WebCore::AVAudioSessionCaptureDeviceManager::audioSessionDeviceWithUID):
+        (WebCore::AVAudioSessionCaptureDeviceManager::refreshAudioCaptureDevices):
+        * platform/mediastream/mac/AVCaptureDeviceManager.mm:
+        (WebCore::AVCaptureDeviceManager::deviceConnected):
+        (WebCore::AVCaptureDeviceManager::deviceDisconnected):
+        * platform/mediastream/mac/CoreAudioCaptureDeviceManager.cpp:
+        (WebCore::CoreAudioCaptureDeviceManager::refreshAudioCaptureDevices):
+        (WebCore::CoreAudioCaptureDeviceManager::devicesChanged):
+
 2017-05-01  Joanmarie Diggs  <jdiggs@igalia.com>
 
         AX: AccessibilityTable::ariaRowCount() and ariaColumnCount() should not return -1 unless that is the author-provided value
index 3416ca4..54680aa 100644 (file)
                CDBEAEAD19D92B6C00BEBA88 /* MediaSelectionGroupAVFObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = CDBEAEAB19D92B6C00BEBA88 /* MediaSelectionGroupAVFObjC.h */; };
                CDC26B40160A8CC60026757B /* LegacyMockCDM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDC26B3C160A62B00026757B /* LegacyMockCDM.cpp */; };
                CDC26B41160A8CCE0026757B /* LegacyMockCDM.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC26B3D160A62B00026757B /* LegacyMockCDM.h */; };
+               CDC675221EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC675201EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.mm */; };
+               CDC675231EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC675211EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.h */; };
+               CDC675261EAEA9D400727C84 /* AVAudioSessionCaptureDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC675241EAEA9D400727C84 /* AVAudioSessionCaptureDevice.mm */; };
+               CDC675271EAEA9D400727C84 /* AVAudioSessionCaptureDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC675251EAEA9D400727C84 /* AVAudioSessionCaptureDevice.h */; };
                CDC69DD61632026C007C38DF /* WebCoreFullScreenWarningView.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC69DD41632026C007C38DF /* WebCoreFullScreenWarningView.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CDC69DD71632026C007C38DF /* WebCoreFullScreenWarningView.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDC69DD51632026C007C38DF /* WebCoreFullScreenWarningView.mm */; };
                CDC69DDA16371FD4007C38DF /* WebCoreFullScreenPlaceholderView.h in Headers */ = {isa = PBXBuildFile; fileRef = CDC69DD816371FD3007C38DF /* WebCoreFullScreenPlaceholderView.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CDC1DD4117CC2C48008CB55D /* mediaControlsApple.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = mediaControlsApple.css; sourceTree = "<group>"; };
                CDC26B3C160A62B00026757B /* LegacyMockCDM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = LegacyMockCDM.cpp; sourceTree = "<group>"; };
                CDC26B3D160A62B00026757B /* LegacyMockCDM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyMockCDM.h; sourceTree = "<group>"; };
+               CDC675201EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AVAudioSessionCaptureDeviceManager.mm; sourceTree = "<group>"; };
+               CDC675211EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVAudioSessionCaptureDeviceManager.h; sourceTree = "<group>"; };
+               CDC675241EAEA9D400727C84 /* AVAudioSessionCaptureDevice.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AVAudioSessionCaptureDevice.mm; sourceTree = "<group>"; };
+               CDC675251EAEA9D400727C84 /* AVAudioSessionCaptureDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AVAudioSessionCaptureDevice.h; sourceTree = "<group>"; };
                CDC69DD41632026C007C38DF /* WebCoreFullScreenWarningView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreFullScreenWarningView.h; sourceTree = "<group>"; };
                CDC69DD51632026C007C38DF /* WebCoreFullScreenWarningView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCoreFullScreenWarningView.mm; sourceTree = "<group>"; };
                CDC69DD816371FD3007C38DF /* WebCoreFullScreenPlaceholderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreFullScreenPlaceholderView.h; sourceTree = "<group>"; };
                        isa = PBXGroup;
                        children = (
                                415747421E38698000E914D8 /* libwebrtc */,
+                               CDC6751F1EAEA99600727C84 /* ios */,
                                0729B14D17CFCCA0004F1D60 /* mac */,
                                07D6A4F61BF2307D00174146 /* AudioTrackPrivateMediaStream.h */,
                                07B7116A1D899E63009F0FFB /* CaptureDevice.h */,
                        name = MediaControls;
                        sourceTree = "<group>";
                };
+               CDC6751F1EAEA99600727C84 /* ios */ = {
+                       isa = PBXGroup;
+                       children = (
+                               CDC675241EAEA9D400727C84 /* AVAudioSessionCaptureDevice.mm */,
+                               CDC675251EAEA9D400727C84 /* AVAudioSessionCaptureDevice.h */,
+                               CDC675201EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.mm */,
+                               CDC675211EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.h */,
+                       );
+                       path = ios;
+                       sourceTree = "<group>";
+               };
                CDF2B005182053DF00F2B424 /* mediasource */ = {
                        isa = PBXGroup;
                        children = (
                                A8EA7CB80A192B9C00A8EF5F /* HTMLHeadingElement.h in Headers */,
                                A8EA7CAF0A192B9C00A8EF5F /* HTMLHRElement.h in Headers */,
                                A871DE270A152AC800B12A68 /* HTMLHtmlElement.h in Headers */,
+                               CDC675271EAEA9D400727C84 /* AVAudioSessionCaptureDevice.h in Headers */,
                                A871DE2A0A152AC800B12A68 /* HTMLIFrameElement.h in Headers */,
                                A8EA7D2D0A19385500A8EF5F /* HTMLImageElement.h in Headers */,
                                A8EA7D2B0A19385500A8EF5F /* HTMLImageLoader.h in Headers */,
                                1A762C7A0A074F2600989F5B /* JSXPathResult.h in Headers */,
                                BCEFE1EB0DCA5F6400739219 /* JSXSLTProcessor.h in Headers */,
                                85031B440A44EFC700F992E0 /* KeyboardEvent.h in Headers */,
+                               CDC675231EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.h in Headers */,
                                1AE00D59182DAC8D00087DD7 /* KeyedCoding.h in Headers */,
                                517A63C51B74318F00E7DCDC /* KeyedDecoderCF.h in Headers */,
                                517A63C61B74319200E7DCDC /* KeyedEncoderCF.h in Headers */,
                                415CDAF41E6B8F87004F11EE /* CanvasCaptureMediaStreamTrack.cpp in Sources */,
                                49484FC1102CF23C00187DD3 /* CanvasGradient.cpp in Sources */,
                                4671E0651D67A59600C6B497 /* CanvasPath.cpp in Sources */,
+                               CDC675261EAEA9D400727C84 /* AVAudioSessionCaptureDevice.mm in Sources */,
                                49484FC4102CF23C00187DD3 /* CanvasPattern.cpp in Sources */,
                                49C7B9DC1042D32F0009D447 /* CanvasRenderingContext.cpp in Sources */,
                                49484FCA102CF23C00187DD3 /* CanvasRenderingContext2D.cpp in Sources */,
                                371E65CE13661EED00BEEDB0 /* PageSerializer.cpp in Sources */,
                                E1284AEA10447DEE00EAEB52 /* PageTransitionEvent.cpp in Sources */,
                                51E1ECC20C91C90400DC255B /* PageURLRecord.cpp in Sources */,
+                               CDC675221EAEA9B700727C84 /* AVAudioSessionCaptureDeviceManager.mm in Sources */,
                                FD3160A212B026F700C1A359 /* Panner.cpp in Sources */,
                                FD31601912B0267600C1A359 /* PannerNode.cpp in Sources */,
                                A18890AE1AA13F250026C301 /* ParentalControlsContentFilter.mm in Sources */,
index cb35c93..3445fae 100644 (file)
@@ -107,4 +107,24 @@ std::optional<CaptureDevice> CaptureDeviceManager::deviceWithUID(const String& d
     return std::nullopt;
 }
 
+static CaptureDeviceManager::ObserverToken nextObserverToken()
+{
+    static CaptureDeviceManager::ObserverToken nextToken = 0;
+    return ++nextToken;
+}
+
+CaptureDeviceManager::ObserverToken CaptureDeviceManager::addCaptureDeviceChangedObserver(CaptureDeviceChangedCallback observer)
+{
+    auto token = nextObserverToken();
+    m_observers.set(token, WTFMove(observer));
+    return token;
+}
+
+void CaptureDeviceManager::removeCaptureDeviceChangedObserver(ObserverToken token)
+{
+    ASSERT(m_observers.contains(token));
+    m_observers.remove(token);
+}
+
+
 #endif // ENABLE(MEDIA_STREAM)
index 5810f49..dff0023 100644 (file)
@@ -34,6 +34,11 @@ namespace WebCore {
 
 class CaptureDeviceManager {
 public:
+    using CaptureDeviceChangedCallback = std::function<void()>;
+    using ObserverToken = uint32_t;
+    virtual ObserverToken addCaptureDeviceChangedObserver(CaptureDeviceChangedCallback);
+    virtual void removeCaptureDeviceChangedObserver(ObserverToken);
+
     virtual Vector<CaptureDevice>& captureDevices() = 0;
     virtual void refreshCaptureDevices() { }
     virtual Vector<CaptureDevice> getAudioSourcesInfo();
@@ -43,6 +48,7 @@ public:
 protected:
     virtual ~CaptureDeviceManager();
     std::optional<CaptureDevice> captureDeviceFromPersistentID(const String& captureDeviceID);
+    HashMap<ObserverToken, CaptureDeviceChangedCallback> m_observers;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.h b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.h
new file mode 100644 (file)
index 0000000..fafc87e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
+
+#include "CaptureDevice.h"
+#include <wtf/RetainPtr.h>
+
+OBJC_CLASS AVAudioSessionPortDescription;
+
+namespace WebCore {
+
+class AVAudioSessionCaptureDevice : public CaptureDevice {
+public:
+    static AVAudioSessionCaptureDevice create(AVAudioSessionPortDescription*);
+    virtual ~AVAudioSessionCaptureDevice() = default;
+
+private:
+    AVAudioSessionCaptureDevice(AVAudioSessionPortDescription*, const String& persistentID, const String& label);
+
+    RetainPtr<AVAudioSessionPortDescription> m_portDescription;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.mm b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDevice.mm
new file mode 100644 (file)
index 0000000..1063df5
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AVAudioSessionCaptureDevice.h"
+
+#if ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
+
+#include <AVFoundation/AVAudioSession.h>
+
+namespace WebCore {
+
+AVAudioSessionCaptureDevice AVAudioSessionCaptureDevice::create(AVAudioSessionPortDescription* portDescription)
+{
+    String persistentID = portDescription.UID;
+    String label = portDescription.portName;
+    return AVAudioSessionCaptureDevice(portDescription, persistentID, label);
+}
+
+AVAudioSessionCaptureDevice::AVAudioSessionCaptureDevice(AVAudioSessionPortDescription* portDescription, const String& persistentID, const String& label)
+    : CaptureDevice(persistentID, CaptureDevice::DeviceType::Audio, label)
+    , m_portDescription(portDescription)
+{
+}
+
+}
+
+#endif // ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.h
new file mode 100644 (file)
index 0000000..a36b9f7
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
+
+#include "CaptureDeviceManager.h"
+#include <wtf/Forward.h>
+#include <wtf/HashSet.h>
+#include <wtf/RetainPtr.h>
+
+OBJC_CLASS WebAVAudioSessionAvailableInputsListener;
+
+namespace WebCore {
+
+class AVAudioSessionCaptureDevice;
+class CaptureDevice;
+
+class AVAudioSessionCaptureDeviceManager final : public CaptureDeviceManager {
+    friend class NeverDestroyed<AVAudioSessionCaptureDeviceManager>;
+public:
+    static AVAudioSessionCaptureDeviceManager& singleton();
+
+    Vector<CaptureDevice>& captureDevices() final;
+
+    Vector<AVAudioSessionCaptureDevice>& audioSessionCaptureDevices();
+    std::optional<AVAudioSessionCaptureDevice> audioSessionDeviceWithUID(const String&);
+
+private:
+    AVAudioSessionCaptureDeviceManager() = default;
+    ~AVAudioSessionCaptureDeviceManager();
+
+    void refreshAudioCaptureDevices();
+
+    std::optional<Vector<CaptureDevice>> m_devices;
+    std::optional<Vector<AVAudioSessionCaptureDevice>> m_audioSessionCaptureDevices;
+    RetainPtr<WebAVAudioSessionAvailableInputsListener> m_listener;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM && PLATFORM(IOS)
diff --git a/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm b/Source/WebCore/platform/mediastream/ios/AVAudioSessionCaptureDeviceManager.mm
new file mode 100644 (file)
index 0000000..48e7524
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AVAudioSessionCaptureDeviceManager.h"
+
+#if ENABLE(MEDIA_STREAM) && PLATFORM(IOS)
+
+#include "AVAudioSessionCaptureDevice.h"
+#include "SoftLinking.h"
+#include <AVFoundation/AVAudioSession.h>
+#include <wtf/Vector.h>
+
+SOFT_LINK_FRAMEWORK(AVFoundation)
+SOFT_LINK_CLASS(AVFoundation, AVAudioSession)
+#define AVAudioSession getAVAudioSessionClass()
+
+void* AvailableInputsContext = &AvailableInputsContext;
+
+@interface WebAVAudioSessionAvailableInputsListener : NSObject {
+    std::function<void()> _callback;
+}
+@end
+
+@implementation WebAVAudioSessionAvailableInputsListener
+- (id)initWithCallback:(std::function<void()>)callback
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _callback = callback;
+    return self;
+}
+
+- (void)invalidate
+{
+    _callback = nullptr;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+    UNUSED_PARAM(keyPath);
+    UNUSED_PARAM(object);
+    UNUSED_PARAM(change);
+    if (context == AvailableInputsContext && _callback)
+        _callback();
+}
+@end
+
+namespace WebCore {
+
+AVAudioSessionCaptureDeviceManager& AVAudioSessionCaptureDeviceManager::singleton()
+{
+    static NeverDestroyed<AVAudioSessionCaptureDeviceManager> manager;
+    return manager;
+}
+
+AVAudioSessionCaptureDeviceManager::~AVAudioSessionCaptureDeviceManager()
+{
+    [m_listener invalidate];
+    m_listener = nullptr;
+}
+
+Vector<CaptureDevice>& AVAudioSessionCaptureDeviceManager::captureDevices()
+{
+    if (!m_devices)
+        refreshAudioCaptureDevices();
+    return m_devices.value();
+}
+
+Vector<AVAudioSessionCaptureDevice>& AVAudioSessionCaptureDeviceManager::audioSessionCaptureDevices()
+{
+    if (!m_audioSessionCaptureDevices)
+        refreshAudioCaptureDevices();
+    return m_audioSessionCaptureDevices.value();
+}
+
+std::optional<AVAudioSessionCaptureDevice> AVAudioSessionCaptureDeviceManager::audioSessionDeviceWithUID(const String& deviceID)
+{
+    if (!m_audioSessionCaptureDevices)
+        refreshAudioCaptureDevices();
+
+    for (auto& device : audioSessionCaptureDevices()) {
+        if (device.persistentId() == deviceID)
+            return device;
+    }
+    return std::nullopt;
+}
+
+void AVAudioSessionCaptureDeviceManager::refreshAudioCaptureDevices()
+{
+    if (!m_listener) {
+        m_listener = adoptNS([[WebAVAudioSessionAvailableInputsListener alloc] initWithCallback:[this] {
+            refreshAudioCaptureDevices();
+        }]);
+        [[AVAudioSession sharedSession] addObserver:m_listener.get() forKeyPath:@"availableInputs" options:0 context:AvailableInputsContext];
+    }
+
+    Vector<AVAudioSessionCaptureDevice> newAudioDevices;
+    Vector<CaptureDevice> newDevices;
+
+    for (AVAudioSessionPortDescription *portDescription in [AVAudioSession sharedInstance].availableInputs) {
+        auto audioDevice = AVAudioSessionCaptureDevice::create(portDescription);
+        newDevices.append({ audioDevice.persistentId(), audioDevice.type(), audioDevice.label(), audioDevice.groupId() });
+        newAudioDevices.append(WTFMove(audioDevice));
+    }
+
+    m_audioSessionCaptureDevices = WTFMove(newAudioDevices);
+    m_devices = WTFMove(newDevices);
+
+    for (auto& observer : m_observers.values())
+        observer();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(MEDIA_STREAM) && PLATFORM(MAC)
index f6474d6..fd759ea 100644 (file)
@@ -194,6 +194,9 @@ void AVCaptureDeviceManager::registerForDeviceNotifications()
 void AVCaptureDeviceManager::deviceConnected()
 {
     refreshCaptureDevices();
+
+    for (auto& observer : m_observers.values())
+        observer();
 }
 
 void AVCaptureDeviceManager::deviceDisconnected(AVCaptureDeviceTypedef* device)
@@ -211,6 +214,9 @@ void AVCaptureDeviceManager::deviceDisconnected(AVCaptureDeviceTypedef* device)
             devices[i].setEnabled(false);
         }
     }
+
+    for (auto& observer : m_observers.values())
+        observer();
 }
 
 } // namespace WebCore
index 6dd3233..8c2ce1b 100644 (file)
@@ -128,18 +128,22 @@ void CoreAudioCaptureDeviceManager::refreshAudioCaptureDevices()
         }
     }
 
-    if (haveDeviceChanges) {
-        m_devices = Vector<CaptureDevice>();
+    if (!haveDeviceChanges)
+        return;
 
-        for (auto &device : m_coreAudioCaptureDevices) {
-            CaptureDevice captureDevice(device.persistentId(), CaptureDevice::DeviceType::Audio, device.label());
-            captureDevice.setEnabled(device.enabled());
-            m_devices.append(captureDevice);
-        }
+    m_devices = Vector<CaptureDevice>();
+
+    for (auto &device : m_coreAudioCaptureDevices) {
+        CaptureDevice captureDevice(device.persistentId(), CaptureDevice::DeviceType::Audio, device.label());
+        captureDevice.setEnabled(device.enabled());
+        m_devices.append(captureDevice);
     }
+
+    for (auto& observer : m_observers.values())
+        observer();
 }
 
-OSStatus CoreAudioCaptureDeviceManager::devicesChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, void *userData)
+OSStatus CoreAudioCaptureDeviceManager::devicesChanged(AudioObjectID, UInt32, const AudioObjectPropertyAddress*, voiduserData)
 {
     static_cast<CoreAudioCaptureDeviceManager*>(userData)->refreshAudioCaptureDevices();
     return 0;