[MediaStream] Add Mac screen capture source
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / DisplayCaptureManagerCocoa.cpp
1 /*
2  * Copyright (C) 2017 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 #include "config.h"
27 #include "DisplayCaptureManagerCocoa.h"
28
29 #if ENABLE(MEDIA_STREAM)
30
31 #include "Logging.h"
32 #include <wtf/NeverDestroyed.h>
33
34 #if PLATFORM(MAC)
35 #include "ScreenDisplayCaptureSourceMac.h"
36 #include <CoreGraphics/CGDirectDisplay.h>
37 #endif
38
39 namespace WebCore {
40
41 #if PLATFORM(MAC)
42 static void displayReconfigurationCallBack(CGDirectDisplayID, CGDisplayChangeSummaryFlags, void* userInfo)
43 {
44     if (userInfo)
45         reinterpret_cast<DisplayCaptureManagerCocoa*>(userInfo)->refreshCaptureDevices();
46 }
47 #endif
48
49 DisplayCaptureManagerCocoa& DisplayCaptureManagerCocoa::singleton()
50 {
51     static NeverDestroyed<DisplayCaptureManagerCocoa> manager;
52     return manager.get();
53 }
54
55 DisplayCaptureManagerCocoa::~DisplayCaptureManagerCocoa()
56 {
57 #if PLATFORM(MAC)
58     if (m_observingDisplayChanges)
59         CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
60 #endif
61 }
62
63 const Vector<CaptureDevice>& DisplayCaptureManagerCocoa::captureDevices()
64 {
65     static bool initialized;
66     if (!initialized) {
67         refreshCaptureDevices();
68
69 #if PLATFORM(MAC)
70         CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
71 #endif
72
73         m_observingDisplayChanges = true;
74         initialized = true;
75     };
76     
77     return m_displays;
78 }
79
80 void DisplayCaptureManagerCocoa::refreshCaptureDevices()
81 {
82 #if PLATFORM(MAC)
83     uint32_t displayCount = 0;
84     auto err = CGGetActiveDisplayList(0, nullptr, &displayCount);
85     if (err) {
86         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get display count", (int)err);
87         return;
88     }
89
90     if (!displayCount) {
91         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned a display count of 0");
92         return;
93     }
94
95     CGDirectDisplayID activeDisplays[displayCount];
96     err = CGGetActiveDisplayList(displayCount, &(activeDisplays[0]), &displayCount);
97     if (err) {
98         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get the active display list", (int)err);
99         return;
100     }
101
102     bool haveDeviceChanges = false;
103     for (auto displayID : activeDisplays) {
104         if (std::any_of(m_displaysInternal.begin(), m_displaysInternal.end(), [displayID](auto& device) { return device.cgDirectDisplayID == displayID; }))
105             continue;
106         haveDeviceChanges = true;
107         m_displaysInternal.append({ displayID, CGDisplayIDToOpenGLDisplayMask(displayID) });
108     }
109
110     for (auto& display : m_displaysInternal) {
111         auto displayMask = CGDisplayIDToOpenGLDisplayMask(display.cgDirectDisplayID);
112         if (display.cgOpenGLDisplayMask != displayMask) {
113             display.cgOpenGLDisplayMask = displayMask;
114             haveDeviceChanges = true;
115         }
116     }
117
118     if (!haveDeviceChanges)
119         return;
120
121     int count = 0;
122     m_displays = Vector<CaptureDevice>();
123     for (auto& device : m_displaysInternal) {
124         CaptureDevice displayDevice(String::number(device.cgDirectDisplayID), CaptureDevice::DeviceType::Screen, makeString("Screen ", String::number(count++)));
125         displayDevice.setEnabled(device.cgOpenGLDisplayMask);
126         m_displays.append(WTFMove(displayDevice));
127     }
128 #endif
129 }
130
131 std::optional<CaptureDevice> DisplayCaptureManagerCocoa::screenCaptureDeviceWithPersistentID(const String& deviceID)
132 {
133 #if PLATFORM(MAC)
134     bool ok;
135     auto displayID = deviceID.toUIntStrict(&ok);
136     if (!ok) {
137         RELEASE_LOG(Media, "Display ID does not convert to 32-bit integer");
138         return std::nullopt;
139     }
140
141     auto actualDisplayID = ScreenDisplayCaptureSourceMac::updateDisplayID(displayID);
142     if (!actualDisplayID)
143         return std::nullopt;
144
145     auto device = CaptureDevice(String::number(actualDisplayID.value()), CaptureDevice::DeviceType::Screen, ASCIILiteral("ScreenCaptureDevice"));
146     device.setEnabled(true);
147
148     return device;
149 #else
150     UNUSED_PARAM(deviceID);
151     return std::nullopt;
152 #endif
153 }
154
155 std::optional<CaptureDevice> DisplayCaptureManagerCocoa::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
156 {
157     switch (type) {
158     case CaptureDevice::DeviceType::Screen:
159         return screenCaptureDeviceWithPersistentID(id);
160         break;
161             
162     case CaptureDevice::DeviceType::Application:
163     case CaptureDevice::DeviceType::Window:
164     case CaptureDevice::DeviceType::Browser:
165         break;
166
167     case CaptureDevice::DeviceType::Camera:
168     case CaptureDevice::DeviceType::Microphone:
169     case CaptureDevice::DeviceType::Unknown:
170         ASSERT_NOT_REACHED();
171         break;
172     }
173
174     return std::nullopt;
175 }
176
177 } // namespace WebCore
178
179 #endif // ENABLE(MEDIA_STREAM)