Add media stream release logging
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / ScreenDisplayCaptureSourceMac.mm
1 /*
2  * Copyright (C) 2017-2019 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 "ScreenDisplayCaptureSourceMac.h"
28
29 #if ENABLE(MEDIA_STREAM) && PLATFORM(MAC)
30
31 #include "GraphicsContextCG.h"
32 #include "ImageBuffer.h"
33 #include "Logging.h"
34 #include "MediaConstraints.h"
35 #include "MediaSampleAVFObjC.h"
36 #include "NotImplemented.h"
37 #include "PlatformLayer.h"
38 #include "RealtimeMediaSourceSettings.h"
39 #include "RealtimeVideoUtilities.h"
40
41 #include "CoreVideoSoftLink.h"
42
43 extern "C" {
44 size_t CGDisplayModeGetPixelsWide(CGDisplayModeRef);
45 size_t CGDisplayModeGetPixelsHigh(CGDisplayModeRef);
46 }
47
48 namespace WebCore {
49
50 static Optional<CGDirectDisplayID> updateDisplayID(CGDirectDisplayID displayID)
51 {
52     uint32_t displayCount = 0;
53     auto err = CGGetActiveDisplayList(0, nullptr, &displayCount);
54     if (err) {
55         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get display count", static_cast<int>(err));
56         return WTF::nullopt;
57     }
58
59     if (!displayCount) {
60         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned a display count of 0");
61         return WTF::nullopt;
62     }
63
64     CGDirectDisplayID activeDisplays[displayCount];
65     err = CGGetActiveDisplayList(displayCount, &(activeDisplays[0]), &displayCount);
66     if (err) {
67         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get the active display list", static_cast<int>(err));
68         return WTF::nullopt;
69     }
70
71     auto displayMask = CGDisplayIDToOpenGLDisplayMask(displayID);
72     for (auto display : activeDisplays) {
73         if (displayMask == CGDisplayIDToOpenGLDisplayMask(display))
74             return display;
75     }
76
77     return WTF::nullopt;
78 }
79
80 CaptureSourceOrError ScreenDisplayCaptureSourceMac::create(String&& deviceID, const MediaConstraints* constraints)
81 {
82     bool ok;
83     auto displayID = deviceID.toUIntStrict(&ok);
84     if (!ok) {
85         RELEASE_LOG(Media, "ScreenDisplayCaptureSourceMac::create: Display ID does not convert to 32-bit integer");
86         return { };
87     }
88
89     auto actualDisplayID = updateDisplayID(displayID);
90     if (!actualDisplayID)
91         return { };
92
93     auto source = adoptRef(*new ScreenDisplayCaptureSourceMac(actualDisplayID.value(), "Screen"_s)); // FIXME: figure out what title to use
94     if (constraints && source->applyConstraints(*constraints))
95         return { };
96
97     return CaptureSourceOrError(WTFMove(source));
98 }
99
100 ScreenDisplayCaptureSourceMac::ScreenDisplayCaptureSourceMac(uint32_t displayID, String&& title)
101     : DisplayCaptureSourceCocoa(WTFMove(title))
102     , m_displayID(displayID)
103 {
104 }
105
106 ScreenDisplayCaptureSourceMac::~ScreenDisplayCaptureSourceMac()
107 {
108     if (m_observingDisplayChanges)
109         CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this);
110
111     m_currentFrame = nullptr;
112 }
113
114 bool ScreenDisplayCaptureSourceMac::createDisplayStream()
115 {
116     static const int screenQueueMaximumLength = 6;
117
118     ALWAYS_LOG_IF(loggerPtr(), LOGIDENTIFIER);
119
120     auto actualDisplayID = updateDisplayID(m_displayID);
121     if (!actualDisplayID) {
122         ERROR_LOG_IF(loggerPtr(), LOGIDENTIFIER, "invalid display ID: ", m_displayID);
123         captureFailed();
124         return false;
125     }
126
127     if (m_displayID != actualDisplayID.value()) {
128         m_displayID = actualDisplayID.value();
129         ALWAYS_LOG_IF(loggerPtr(), LOGIDENTIFIER, "display ID changed to ", static_cast<int>(m_displayID));
130         m_displayStream = nullptr;
131     }
132
133     if (!m_displayStream) {
134         auto displayMode = adoptCF(CGDisplayCopyDisplayMode(m_displayID));
135         auto screenWidth = CGDisplayModeGetPixelsWide(displayMode.get());
136         auto screenHeight = CGDisplayModeGetPixelsHigh(displayMode.get());
137         if (!screenWidth || !screenHeight) {
138             ERROR_LOG_IF(loggerPtr(), LOGIDENTIFIER, "unable to get screen width/height");
139             captureFailed();
140             return false;
141         }
142         setIntrinsicSize(IntSize(screenWidth, screenHeight));
143
144         if (!m_captureQueue)
145             m_captureQueue = adoptOSObject(dispatch_queue_create("ScreenDisplayCaptureSourceMac Capture Queue", DISPATCH_QUEUE_SERIAL));
146
147         NSDictionary* streamOptions = @{
148             (__bridge NSString *)kCGDisplayStreamMinimumFrameTime : @(1 / frameRate()),
149             (__bridge NSString *)kCGDisplayStreamQueueDepth : @(screenQueueMaximumLength),
150             (__bridge NSString *)kCGDisplayStreamColorSpace : (__bridge id)sRGBColorSpaceRef(),
151             (__bridge NSString *)kCGDisplayStreamShowCursor : @(YES),
152         };
153
154         auto weakThis = makeWeakPtr(*this);
155         auto frameAvailableBlock = ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
156             if (!weakThis)
157                 return;
158
159             weakThis->frameAvailable(status, displayTime, frameSurface, updateRef);
160         };
161
162         m_displayStream = adoptCF(CGDisplayStreamCreateWithDispatchQueue(m_displayID, screenWidth, screenHeight, preferedPixelBufferFormat(), (__bridge CFDictionaryRef)streamOptions, m_captureQueue.get(), frameAvailableBlock));
163         if (!m_displayStream) {
164             ERROR_LOG_IF(loggerPtr(), LOGIDENTIFIER, "CGDisplayStreamCreate failed");
165             captureFailed();
166             return false;
167         }
168     }
169
170     if (!m_observingDisplayChanges) {
171         CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this);
172         m_observingDisplayChanges = true;
173     }
174
175     return true;
176 }
177
178 void ScreenDisplayCaptureSourceMac::startProducingData()
179 {
180     ALWAYS_LOG_IF(loggerPtr(), LOGIDENTIFIER);
181     DisplayCaptureSourceCocoa::startProducingData();
182
183     if (m_isRunning)
184         return;
185
186     startDisplayStream();
187 }
188
189 void ScreenDisplayCaptureSourceMac::stopProducingData()
190 {
191     ALWAYS_LOG_IF(loggerPtr(), LOGIDENTIFIER);
192     DisplayCaptureSourceCocoa::stopProducingData();
193
194     if (!m_isRunning)
195         return;
196
197     if (m_displayStream)
198         CGDisplayStreamStop(m_displayStream.get());
199
200     m_isRunning = false;
201 }
202
203 DisplayCaptureSourceCocoa::DisplayFrameType ScreenDisplayCaptureSourceMac::generateFrame()
204 {
205     DisplaySurface currentFrame;
206     {
207         LockHolder lock(m_currentFrameMutex);
208         currentFrame = m_currentFrame.ioSurface();
209     }
210
211     return DisplayCaptureSourceCocoa::DisplayFrameType { RetainPtr<IOSurfaceRef> { currentFrame.ioSurface() } };
212 }
213
214 void ScreenDisplayCaptureSourceMac::startDisplayStream()
215 {
216     auto actualDisplayID = updateDisplayID(m_displayID);
217     if (!actualDisplayID)
218         return;
219
220     if (m_displayID != actualDisplayID.value()) {
221         m_displayID = actualDisplayID.value();
222         ALWAYS_LOG_IF(loggerPtr(), LOGIDENTIFIER, "display ID changed to ", static_cast<int>(m_displayID));
223     }
224
225     if (!m_displayStream && !createDisplayStream())
226         return;
227
228     auto err = CGDisplayStreamStart(m_displayStream.get());
229     if (err) {
230         ERROR_LOG_IF(loggerPtr(), LOGIDENTIFIER, "CGDisplayStreamStart failed with error ", static_cast<int>(err));
231         captureFailed();
232         return;
233     }
234
235     m_isRunning = true;
236 }
237
238 void ScreenDisplayCaptureSourceMac::commitConfiguration()
239 {
240     if (m_isRunning && !m_displayStream)
241         startDisplayStream();
242 }
243
244 void ScreenDisplayCaptureSourceMac::displayWasReconfigured(CGDirectDisplayID, CGDisplayChangeSummaryFlags)
245 {
246     // FIXME: implement!
247 }
248
249 void ScreenDisplayCaptureSourceMac::displayReconfigurationCallBack(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo)
250 {
251     if (userInfo)
252         reinterpret_cast<ScreenDisplayCaptureSourceMac *>(userInfo)->displayWasReconfigured(display, flags);
253 }
254
255 void ScreenDisplayCaptureSourceMac::frameAvailable(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef)
256 {
257     switch (status) {
258     case kCGDisplayStreamFrameStatusFrameComplete:
259         break;
260
261     case kCGDisplayStreamFrameStatusFrameIdle:
262         break;
263
264     case kCGDisplayStreamFrameStatusFrameBlank:
265         RELEASE_LOG(Media, "ScreenDisplayCaptureSourceMac::frameAvailable: kCGDisplayStreamFrameStatusFrameBlank");
266         break;
267
268     case kCGDisplayStreamFrameStatusStopped:
269         RELEASE_LOG(Media, "ScreenDisplayCaptureSourceMac::frameAvailable: kCGDisplayStreamFrameStatusStopped");
270         break;
271     }
272
273     if (!frameSurface || !displayTime)
274         return;
275
276     size_t count;
277     auto* rects = CGDisplayStreamUpdateGetRects(updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
278     if (!rects || !count)
279         return;
280
281     LockHolder lock(m_currentFrameMutex);
282     m_currentFrame = frameSurface;
283 }
284
285 Optional<CaptureDevice> ScreenDisplayCaptureSourceMac::screenCaptureDeviceWithPersistentID(const String& deviceID)
286 {
287     bool ok;
288     auto displayID = deviceID.toUIntStrict(&ok);
289     if (!ok) {
290         RELEASE_LOG(Media, "ScreenDisplayCaptureSourceMac::screenCaptureDeviceWithPersistentID: display ID does not convert to 32-bit integer");
291         return WTF::nullopt;
292     }
293
294     auto actualDisplayID = updateDisplayID(displayID);
295     if (!actualDisplayID)
296         return WTF::nullopt;
297
298     auto device = CaptureDevice(String::number(actualDisplayID.value()), CaptureDevice::DeviceType::Screen, "ScreenCaptureDevice"_s);
299     device.setEnabled(true);
300
301     return device;
302 }
303
304 void ScreenDisplayCaptureSourceMac::screenCaptureDevices(Vector<CaptureDevice>& displays)
305 {
306     uint32_t displayCount = 0;
307     auto err = CGGetActiveDisplayList(0, nullptr, &displayCount);
308     if (err) {
309         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get display count", (int)err);
310         return;
311     }
312
313     if (!displayCount) {
314         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned a display count of 0");
315         return;
316     }
317
318     CGDirectDisplayID activeDisplays[displayCount];
319     err = CGGetActiveDisplayList(displayCount, &(activeDisplays[0]), &displayCount);
320     if (err) {
321         RELEASE_LOG(Media, "CGGetActiveDisplayList() returned error %d when trying to get the active display list", (int)err);
322         return;
323     }
324
325     int count = 0;
326     for (auto displayID : activeDisplays) {
327         CaptureDevice displayDevice(String::number(displayID), CaptureDevice::DeviceType::Screen, makeString("Screen ", String::number(count++)));
328         displayDevice.setEnabled(CGDisplayIDToOpenGLDisplayMask(displayID));
329         displays.append(WTFMove(displayDevice));
330     }
331 }
332
333 } // namespace WebCore
334
335 #endif // ENABLE(MEDIA_STREAM) && PLATFORM(MAC)