diff --git a/CMakePresets.json b/CMakePresets.json
index 83ed2dc3..1f0dc222 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -51,7 +51,6 @@
"name": "hyperion-bare-minimum",
"hidden": true,
"cacheVariables": {
- // Disable Grabbers
"ENABLE_AMLOGIC": "OFF",
"ENABLE_DDA": "OFF",
"ENABLE_DISPMANX": "OFF",
@@ -64,8 +63,6 @@
"ENABLE_X11": "OFF",
"ENABLE_XCB": "OFF",
"ENABLE_AUDIO": "OFF",
-
- // LED-Devices
"ENABLE_DEV_FTDI": "OFF",
"ENABLE_DEV_NETWORK": "OFF",
"ENABLE_DEV_SERIAL": "ON",
@@ -73,23 +70,16 @@
"ENABLE_DEV_TINKERFORGE": "OFF",
"ENABLE_DEV_USB_HID": "OFF",
"ENABLE_DEV_WS281XPWM": "OFF",
-
- // Disable Input Servers
"ENABLE_BOBLIGHT_SERVER": "OFF",
"ENABLE_CEC": "OFF",
"ENABLE_FLATBUF_SERVER": "OFF",
"ENABLE_PROTOBUF_SERVER": "OFF",
-
- // Disable Output Connectors
"ENABLE_FORWARDER": "OFF",
"ENABLE_FLATBUF_CONNECT": "OFF",
-
- // Disable Services
"ENABLE_EXPERIMENTAL": "OFF",
"ENABLE_MDNS": "OFF",
"ENABLE_REMOTE_CTL": "OFF",
"ENABLE_EFFECTENGINE": "OFF",
-
"ENABLE_JSONCHECKS": "ON",
"ENABLE_DEPLOY_DEPENDENCIES": "ON"
}
diff --git a/cmake/osxbundle/Info.plist.in b/cmake/osxbundle/Info.plist.in
index 9774dd79..d6c77d50 100644
--- a/cmake/osxbundle/Info.plist.in
+++ b/cmake/osxbundle/Info.plist.in
@@ -26,6 +26,8 @@
APPL
LSUIElement
1
+ NSCameraUsageDescription
+ Hyperion uses this access to record screencasts
NSHumanReadableCopyright
${MACOSX_BUNDLE_COPYRIGHT}
Source Code
diff --git a/include/grabber/osx/OsxFrameGrabber.h b/include/grabber/osx/OsxFrameGrabber.h
index afb430fc..1adc3696 100644
--- a/include/grabber/osx/OsxFrameGrabber.h
+++ b/include/grabber/osx/OsxFrameGrabber.h
@@ -1,6 +1,6 @@
#pragma once
-// OSX includes
+// CoreGraphics
#include
// Utils includes
diff --git a/libsrc/grabber/osx/CMakeLists.txt b/libsrc/grabber/osx/CMakeLists.txt
index 7d18e8d3..a5f01acb 100644
--- a/libsrc/grabber/osx/CMakeLists.txt
+++ b/libsrc/grabber/osx/CMakeLists.txt
@@ -1,10 +1,28 @@
add_library(osx-grabber
${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxFrameGrabber.h
${CMAKE_SOURCE_DIR}/include/grabber/osx/OsxWrapper.h
- ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxFrameGrabber.cpp
+ ${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxFrameGrabber.mm
${CMAKE_SOURCE_DIR}/libsrc/grabber/osx/OsxWrapper.cpp
)
target_link_libraries(osx-grabber
hyperion
+ "$"
)
+
+file(WRITE ${CMAKE_BINARY_DIR}/tmp/SDK15Available.c
+ "#include
+ #if __MAC_OS_X_VERSION_MAX_ALLOWED < 150000
+ #error __MAC_OS_X_VERSION_MAX_ALLOWED < 150000
+ #endif
+ int main(int argc, char** argv)
+ {
+ return 0;
+ }"
+)
+
+try_compile(SDK_15_AVAILABLE ${CMAKE_BINARY_DIR} SOURCES ${CMAKE_BINARY_DIR}/tmp/SDK15Available.c)
+if(SDK_15_AVAILABLE)
+ target_compile_definitions(osx-grabber PRIVATE SDK_15_AVAILABLE)
+ target_link_libraries(osx-grabber "$")
+endif()
diff --git a/libsrc/grabber/osx/OsxFrameGrabber.cpp b/libsrc/grabber/osx/OsxFrameGrabber.mm
similarity index 56%
rename from libsrc/grabber/osx/OsxFrameGrabber.cpp
rename to libsrc/grabber/osx/OsxFrameGrabber.mm
index 916402c2..ef537c16 100644
--- a/libsrc/grabber/osx/OsxFrameGrabber.cpp
+++ b/libsrc/grabber/osx/OsxFrameGrabber.mm
@@ -1,10 +1,16 @@
// STL includes
#include
#include
+#include
-// Local includes
+// Header
#include
+// ScreenCaptureKit
+#if defined(SDK_15_AVAILABLE)
+#include
+#endif
+
//Qt
#include
#include
@@ -15,9 +21,70 @@ namespace {
const bool verbose = false;
} //End of constants
+#if defined(SDK_15_AVAILABLE)
+ static CGImageRef capture15(CGDirectDisplayID id, CGRect diIntersectDisplayLocal)
+ {
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+ __block CGImageRef image1 = nil;
+ [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error)
+ {
+ @autoreleasepool
+ {
+ if (error || !content)
+ {
+ dispatch_semaphore_signal(semaphore);
+ return;
+ }
+
+ SCDisplay* target = nil;
+ for (SCDisplay *display in content.displays)
+ {
+ if (display.displayID == id)
+ {
+ target = display;
+ break;
+ }
+ }
+ if (!target)
+ {
+ dispatch_semaphore_signal(semaphore);
+ return;
+ }
+
+ SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]];
+ SCStreamConfiguration* config = [[SCStreamConfiguration alloc] init];
+ config.queueDepth = 5;
+ config.sourceRect = diIntersectDisplayLocal;
+ config.scalesToFit = false;
+ config.captureResolution = SCCaptureResolutionBest;
+
+ CGDisplayModeRef modeRef = CGDisplayCopyDisplayMode(id);
+ double sysScale = CGDisplayModeGetPixelWidth(modeRef) / CGDisplayModeGetWidth(modeRef);
+ config.width = diIntersectDisplayLocal.size.width * sysScale;
+ config.height = diIntersectDisplayLocal.size.height * sysScale;
+
+ [SCScreenshotManager captureImageWithFilter:filter
+ configuration:config
+ completionHandler:^(CGImageRef img, NSError* error)
+ {
+ if (!error)
+ {
+ image1 = CGImageCreateCopyWithColorSpace(img, CGColorSpaceCreateDeviceRGB());
+ }
+ dispatch_semaphore_signal(semaphore);
+ }];
+ }
+ }];
+
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+ dispatch_release(semaphore);
+ return image1;
+ }
+#endif
+
OsxFrameGrabber::OsxFrameGrabber(int display)
: Grabber("GRABBER-OSX")
- , _screenIndex(display)
+ , _screenIndex(display)
{
_isEnabled = false;
_useImageResampler = true;
@@ -31,6 +98,15 @@ bool OsxFrameGrabber::setupDisplay()
{
bool rc (false);
+#if defined(SDK_15_AVAILABLE)
+ if (!CGPreflightScreenCaptureAccess())
+ {
+ if(!CGRequestScreenCaptureAccess())
+ Error(_log, "Screen capture permission required to start the grabber");
+ return false;
+ }
+#endif
+
rc = setDisplayIndex(_screenIndex);
return rc;
@@ -41,39 +117,38 @@ int OsxFrameGrabber::grabFrame(Image & image)
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
-
CGImageRef dispImage;
- CFDataRef imgData;
- unsigned char * pImgData;
- unsigned dspWidth;
- unsigned dspHeight;
- dispImage = CGDisplayCreateImage(_display);
+ #if defined(SDK_15_AVAILABLE)
+ dispImage = capture15(_display, CGDisplayBounds(_display));
+ #else
+ dispImage = CGDisplayCreateImageForRect(_display, CGDisplayBounds(_display));
+ #endif
// display lost, use main
if (dispImage == nullptr && _display != 0)
{
- dispImage = CGDisplayCreateImage(kCGDirectMainDisplay);
- // no displays connected, return
- if (dispImage == nullptr)
- {
- Error(_log, "No display connected...");
- return -1;
- }
+ #if defined(SDK_15_AVAILABLE)
+ dispImage = capture15(kCGDirectMainDisplay, CGDisplayBounds(kCGDirectMainDisplay));
+ #else
+ dispImage = CGDisplayCreateImageForRect(kCGDirectMainDisplay, CGDisplayBounds(kCGDirectMainDisplay));
+ #endif
}
- imgData = CGDataProviderCopyData(CGImageGetDataProvider(dispImage));
- pImgData = (unsigned char*) CFDataGetBytePtr(imgData);
- dspWidth = CGImageGetWidth(dispImage);
- dspHeight = CGImageGetHeight(dispImage);
- _imageResampler.processImage( pImgData,
- static_cast(dspWidth),
- static_cast(dspHeight),
- static_cast(CGImageGetBytesPerRow(dispImage)),
- PixelFormat::BGR32,
- image);
+ // no displays connected, return
+ if (dispImage == nullptr)
+ {
+ Error(_log, "No display connected...");
+ return -1;
+ }
+
+ CFDataRef imgData = CGDataProviderCopyData(CGImageGetDataProvider(dispImage));
+ if (imgData != nullptr)
+ {
+ _imageResampler.processImage((uint8_t *)CFDataGetBytePtr(imgData), static_cast(CGImageGetWidth(dispImage)), static_cast(CGImageGetHeight(dispImage)), static_cast(CGImageGetBytesPerRow(dispImage)), PixelFormat::BGR32, image);
+ CFRelease(imgData);
+ }
- CFRelease(imgData);
CGImageRelease(dispImage);
}
@@ -108,7 +183,12 @@ bool OsxFrameGrabber::setDisplayIndex(int index)
{
_display = activeDspys[_screenIndex];
- image = CGDisplayCreateImage(_display);
+ #if defined(SDK_15_AVAILABLE)
+ image = capture15(_display, CGDisplayBounds(_display));
+ #else
+ image = CGDisplayCreateImageForRect(_display, CGDisplayBounds(_display));
+ #endif
+
if(image == nullptr)
{
setEnabled(false);