blob: 1e8ec47efbe0d315f9ea4af6d5d8221f93f37912 [file] [log] [blame]
/*
* Copyright (C) 2018 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. ``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
* 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.
*/
#import "config.h"
#import "WindowDisplayCaptureSourceMac.h"
#if ENABLE(MEDIA_STREAM) && PLATFORM(MAC)
#import "GraphicsContextCG.h"
#import "ImageBuffer.h"
#import "Logging.h"
#import "MediaConstraints.h"
#import "MediaSampleAVFObjC.h"
#import "NotImplemented.h"
#import "PixelBufferConformerCV.h"
#import "PlatformLayer.h"
#import "RealtimeMediaSourceSettings.h"
#import <pal/cf/CoreMediaSoftLink.h>
#import <pal/spi/cg/CoreGraphicsSPI.h>
#import <wtf/cf/TypeCastsCF.h>
#import "CoreVideoSoftLink.h"
WTF_DECLARE_CF_TYPE_TRAIT(CGImage);
namespace WebCore {
using namespace PAL;
static bool anyOfCGWindow(const Function<bool(CFDictionaryRef info, unsigned id, const String& title)>& predicate)
{
auto windows = adoptCF(CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID));
if (!windows) {
RELEASE_LOG(Media, "CGWindowListCopyWindowInfo returned NULL");
return false;
}
auto windowCount = CFArrayGetCount(windows.get());
for (auto i = 0; i < windowCount; i++) {
auto windowInfo = checked_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows.get(), i));
if (!windowInfo)
continue;
// Menus, the dock, etc have layers greater than 0, skip them.
auto windowLayerRef = checked_cf_cast<CFNumberRef>(CFDictionaryGetValue(windowInfo, kCGWindowLayer));
if (!windowLayerRef)
continue;
unsigned windowLayer;
CFNumberGetValue(windowLayerRef, kCFNumberIntType, &windowLayer);
if (windowLayer)
continue;
auto onScreen = checked_cf_cast<CFBooleanRef>(CFDictionaryGetValue(windowInfo, kCGWindowIsOnscreen));
if (!CFBooleanGetValue(onScreen))
continue;
auto windowIDRef = checked_cf_cast<CFNumberRef>(CFDictionaryGetValue(windowInfo, kCGWindowNumber));
if (!windowIDRef)
continue;
unsigned windowID;
CFNumberGetValue(windowIDRef, kCFNumberIntType, &windowID);
if (!windowID)
continue;
auto windowTitle = checked_cf_cast<CFStringRef>(CFDictionaryGetValue(windowInfo, kCGWindowName));
if (predicate(windowInfo, windowID, windowTitle))
return true;
}
return false;
}
static RetainPtr<CFDictionaryRef> windowDescription(CGWindowID id)
{
auto ids = adoptCF(CFArrayCreate(nullptr, reinterpret_cast<const void**>(&id), 1, nullptr));
auto windows = adoptCF(CGWindowListCreateDescriptionFromArray(ids.get()));
if (!windows)
return nullptr;
return checked_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(windows.get(), 0));
}
CaptureSourceOrError WindowDisplayCaptureSourceMac::create(String&& windowID, const MediaConstraints* constraints)
{
bool ok;
auto actualID = windowID.toUIntStrict(&ok);
if (!ok) {
RELEASE_LOG(Media, "WindowDisplayCaptureSourceMac::create: window ID does not convert to 32-bit integer");
return { };
}
auto windowInfo = windowDescription(actualID);
if (!windowInfo) {
RELEASE_LOG(Media, "WindowDisplayCaptureSourceMac::create: invalid window ID");
return { };
}
auto source = adoptRef(*new WindowDisplayCaptureSourceMac(actualID, checked_cf_cast<CFStringRef>(CFDictionaryGetValue(windowInfo.get(), kCGWindowName))));
if (constraints && source->applyConstraints(*constraints))
return { };
return CaptureSourceOrError(WTFMove(source));
}
WindowDisplayCaptureSourceMac::WindowDisplayCaptureSourceMac(uint32_t windowID, String&& title)
: DisplayCaptureSourceCocoa(WTFMove(title))
, m_windowID(windowID)
{
}
RetainPtr<CGImageRef> WindowDisplayCaptureSourceMac::windowImage()
{
auto image = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, m_windowID, kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque));
if (!image)
RELEASE_LOG(Media, "WindowDisplayCaptureSourceMac::windowImage: failed to capture window image");
return image;
}
DisplayCaptureSourceCocoa::DisplayFrameType WindowDisplayCaptureSourceMac::generateFrame()
{
return DisplayCaptureSourceCocoa::DisplayFrameType { RetainPtr<CGImageRef> { windowImage() } };
}
Optional<CaptureDevice> WindowDisplayCaptureSourceMac::windowCaptureDeviceWithPersistentID(const String& idString)
{
bool ok;
auto windowID = idString.toUIntStrict(&ok);
if (!ok) {
RELEASE_LOG(Media, "WindowDisplayCaptureSourceMac::windowCaptureDeviceWithPersistentID: window ID does not convert to 32-bit integer");
return WTF::nullopt;
}
String windowTitle;
if (!anyOfCGWindow([&windowTitle, windowID] (CFDictionaryRef, CGWindowID id, const String& title) {
if (windowID != id)
return false;
windowTitle = title;
return true;
})) {
RELEASE_LOG(Media, "WindowDisplayCaptureSourceMac::windowCaptureDeviceWithPersistentID: window ID is not valid");
return WTF::nullopt;
}
auto device = CaptureDevice(String::number(windowID), CaptureDevice::DeviceType::Window, windowTitle);
device.setEnabled(true);
return device;
}
void WindowDisplayCaptureSourceMac::windowCaptureDevices(Vector<CaptureDevice>& windows)
{
anyOfCGWindow([&] (CFDictionaryRef, int id, const String& title) mutable {
CaptureDevice device(String::number(id), CaptureDevice::DeviceType::Window, title);
device.setEnabled(true);
windows.append(WTFMove(device));
return false;
});
}
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM) && PLATFORM(MAC)