From fa32e0f4227e971272a3abe77663165b1a0009eb Mon Sep 17 00:00:00 2001 From: Awawa <69086569+awawa-dev@users.noreply.github.com> Date: Sun, 17 Nov 2024 04:13:48 +0100 Subject: [PATCH] macOS 15 sequoia: migrate to ScreenCaptureKit (#984) --- CMakeLists.txt | 35 ++++- include/grabber/osx/macOS/macOsGrabber.h | 2 + sources/grabber/osx/AVF/AVFGrabber.mm | 12 +- sources/grabber/osx/AVF/CMakeLists.txt | 4 + sources/grabber/osx/macOS/CMakeLists.txt | 4 + sources/grabber/osx/macOS/macOsGrabber.mm | 132 ++++++++++++++---- sources/hyperhdr/CMakeLists.txt | 3 + sources/sound-capture/macos/CMakeLists.txt | 4 + .../sound-capture/macos/SoundCaptureMacOS.mm | 12 +- 9 files changed, 178 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3e72dea5..907b445d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,10 +194,41 @@ if ( "${PLATFORM}" MATCHES "osx" ) list(APPEND CMAKE_PREFIX_PATH "/usr/local/opt/qt5") endif() - SET ( DEFAULT_AVF ON ) - SET ( DEFAULT_MAC_SYSTEM ON ) + SET ( DEFAULT_AVF ON ) SET ( DEFAULT_EMBEDDED_WEB_RESOURCES OFF ) + if (CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + else() + set(VERSION_REPLACEMENT ${CMAKE_OSX_SYSROOT}) + string(REPLACE "/SDKs/MacOSX" "." VERSION_REPLACEMENT ${VERSION_REPLACEMENT}) + string(REPLACE "." ";" VERSION_REPLACEMENT_LIST ${VERSION_REPLACEMENT}) + list(LENGTH VERSION_REPLACEMENT_LIST VERSION_REPLACEMENT_LIST_LENGTH) + if (${VERSION_REPLACEMENT_LIST_LENGTH} GREATER_EQUAL 1) + list (GET VERSION_REPLACEMENT_LIST 1 CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET) + endif() + endif(CMAKE_OSX_DEPLOYMENT_TARGET) + + if (CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET) + message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET}") + SET(CMAKE_OSX_DEPLOYMENT_TARGET_COPY ${CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET}) + string(REPLACE "." ";" CMAKE_OSX_DEPLOYMENT_TARGET_COPY_LIST ${CMAKE_OSX_DEPLOYMENT_TARGET_COPY}) + list(GET CMAKE_OSX_DEPLOYMENT_TARGET_COPY_LIST 0 MACOS_MAJOR_VERSION) + message(STATUS "MAJOR MACOS VERSION: ${MACOS_MAJOR_VERSION}") + + if (${MACOS_MAJOR_VERSION} GREATER_EQUAL "15") + message(STATUS "Looking for: ScreenCaptureKit") + find_library(MACOS_SCK ScreenCaptureKit) + if (MACOS_SCK) + SET ( DEFAULT_MAC_SYSTEM ON ) + else() + message(WARNING "Could not find: ScreenCaptureKit") + endif(MACOS_SCK) + else() + SET ( DEFAULT_MAC_SYSTEM ON ) + endif() + endif(CMAKE_OSX_HYPERHDR_DEPLOYMENT_TARGET) + elseif ( "${PLATFORM}" MATCHES "rpi" ) SET ( DEFAULT_WS281XPWM ON ) SET ( DEFAULT_CEC ON ) diff --git a/include/grabber/osx/macOS/macOsGrabber.h b/include/grabber/osx/macOS/macOsGrabber.h index da8c8b647..3257346eb 100644 --- a/include/grabber/osx/macOS/macOsGrabber.h +++ b/include/grabber/osx/macOS/macOsGrabber.h @@ -65,6 +65,8 @@ public slots: bool init_device(QString selectedDeviceName); void processFrame(int8_t* source); + + void decodeFrame(CGImageRef capturedImage); private: QString _configurationPath; diff --git a/sources/grabber/osx/AVF/AVFGrabber.mm b/sources/grabber/osx/AVF/AVFGrabber.mm index 838b213d2..fd2c33cad 100644 --- a/sources/grabber/osx/AVF/AVFGrabber.mm +++ b/sources/grabber/osx/AVF/AVFGrabber.mm @@ -411,7 +411,11 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB _deviceProperties.clear(); AVCaptureDeviceDiscoverySession* session = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternalUnknown] + #ifndef MACOS_VERSION_14_UP + discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternalUnknown] + #else + discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternal] + #endif mediaType:AVMediaTypeVideo position : AVCaptureDevicePositionUnspecified]; @@ -516,7 +520,11 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB for (AVCaptureDevice* device in[AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternalUnknown] + #ifndef MACOS_VERSION_14_UP + discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternalUnknown] + #else + discoverySessionWithDeviceTypes : @[AVCaptureDeviceTypeExternal] + #endif mediaType:AVMediaTypeVideo position : AVCaptureDevicePositionUnspecified].devices) { diff --git a/sources/grabber/osx/AVF/CMakeLists.txt b/sources/grabber/osx/AVF/CMakeLists.txt index ec6c53336..f689fe4ed 100644 --- a/sources/grabber/osx/AVF/CMakeLists.txt +++ b/sources/grabber/osx/AVF/CMakeLists.txt @@ -11,6 +11,10 @@ target_link_libraries(AVF-grabber Qt${Qt_VERSION}::Network ) +if (${MACOS_MAJOR_VERSION} GREATER_EQUAL "14") + target_compile_definitions(AVF-grabber PUBLIC MACOS_VERSION_14_UP) +endif() + if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) target_precompile_headers(AVF-grabber REUSE_FROM precompiled_hyperhdr_headers) endif() diff --git a/sources/grabber/osx/macOS/CMakeLists.txt b/sources/grabber/osx/macOS/CMakeLists.txt index d7cd0dbc7..9c84d5157 100644 --- a/sources/grabber/osx/macOS/CMakeLists.txt +++ b/sources/grabber/osx/macOS/CMakeLists.txt @@ -11,6 +11,10 @@ target_link_libraries(MACOS-grabber Qt${Qt_VERSION}::Network ) +if(MACOS_SCK) + target_compile_definitions(MACOS-grabber PUBLIC MACOS_SCK) +endif() + if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers) target_precompile_headers(MACOS-grabber REUSE_FROM precompiled_hyperhdr_headers) endif() diff --git a/sources/grabber/osx/macOS/macOsGrabber.mm b/sources/grabber/osx/macOS/macOsGrabber.mm index 0fda8e845..ea49450d7 100644 --- a/sources/grabber/osx/macOS/macOsGrabber.mm +++ b/sources/grabber/osx/macOS/macOsGrabber.mm @@ -49,7 +49,18 @@ #import #import -id activity; +#ifdef MACOS_SCK + #import +#endif + +namespace +{ + id activity; + #ifdef MACOS_SCK + CGColorSpaceRef colorSpaceRgb = nil; + #endif +}; + macOsGrabber::macOsGrabber(const QString& device, const QString& configurationPath) : Grabber(configurationPath, "MACOS_SYSTEM:" + device.left(14)) @@ -99,6 +110,14 @@ Debug(_log, "Uninit grabber: %s", QSTRING_CSTR(_deviceName)); } + #ifdef MACOS_SCK + if (colorSpaceRgb) + { + CGColorSpaceRelease(colorSpaceRgb); + colorSpaceRgb = nil; + } + #endif + _actualDisplay = 0; _initialized = false; @@ -123,6 +142,13 @@ autoDiscovery = true; } + #ifdef MACOS_SCK + if (!colorSpaceRgb) + { + colorSpaceRgb = CGColorSpaceCreateDeviceRGB(); + } + #endif + if (autoDiscovery) { Debug(_log, "Forcing auto discovery device"); @@ -228,13 +254,82 @@ return true; } -void macOsGrabber::grabFrame() +void macOsGrabber::decodeFrame(CGImageRef capturedImage) { - bool stopNow = false; - + CFDataRef sysData = CGDataProviderCopyData(CGImageGetDataProvider(capturedImage)); + + if (sysData != NULL) + { + uint8_t* rawData = (uint8_t*)CFDataGetBytePtr(sysData); + _actualWidth = CGImageGetWidth(capturedImage); + _actualHeight = CGImageGetHeight(capturedImage); + + size_t bytesPerRow = CGImageGetBytesPerRow(capturedImage); + processSystemFrameBGRA(rawData, static_cast(bytesPerRow)); + + CFRelease(sysData); + } +} + +void macOsGrabber::grabFrame() +{ + #ifdef MACOS_SCK + [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* content, NSError* error) + { + SCDisplay* target = nil; + for (SCDisplay *display in content.displays) + { + if (display.displayID == _actualDisplay) + { + target = display; + break; + } + } + if (!target) + { + uninit(); + QTimer::singleShot(3000, this, &macOsGrabber::start); + } + else + { + CGDisplayModeRef displayReference = CGDisplayCopyDisplayMode(_actualDisplay); + double scaleAspect = CGDisplayModeGetPixelWidth(displayReference)/static_cast(CGDisplayModeGetWidth(displayReference)); + CGDisplayModeRelease(displayReference); + + CGRect displayBounds = CGDisplayBounds(_actualDisplay); + + SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:target excludingWindows:@[]]; + SCStreamConfiguration* streamConfig = [[SCStreamConfiguration alloc] init]; + + streamConfig.width = displayBounds.size.width * scaleAspect; + streamConfig.height = displayBounds.size.height * scaleAspect; + streamConfig.sourceRect = displayBounds; + streamConfig.captureResolution = SCCaptureResolutionBest; + streamConfig.scalesToFit = false; + streamConfig.queueDepth = 1; + + [SCScreenshotManager captureImageWithFilter:filter + configuration:streamConfig + completionHandler:^(CGImageRef sourceImg, NSError* error) + { + if (!error) + { + CGImageRef capturedImage = CGImageCreateCopyWithColorSpace(sourceImg, colorSpaceRgb); + + decodeFrame(capturedImage); + + CGImageRelease(capturedImage); + } + } + ]; + + [streamConfig release]; + [filter release]; + } + }]; + #else + bool stopNow = false; CGImageRef display; - CFDataRef sysData; - uint8_t* rawData; display = CGDisplayCreateImage(_actualDisplay); @@ -245,28 +340,17 @@ } else { - sysData = CGDataProviderCopyData(CGImageGetDataProvider(display));; - - if (sysData != NULL) - { - rawData = (uint8_t*)CFDataGetBytePtr(sysData); - _actualWidth = CGImageGetWidth(display); - _actualHeight = CGImageGetHeight(display); - - size_t bytesPerRow = CGImageGetBytesPerRow(display); - processSystemFrameBGRA(rawData, static_cast(bytesPerRow)); - - CFRelease(sysData); - } + decodeFrame(display); CGImageRelease(display); } - if (stopNow) - { - uninit(); - QTimer::singleShot(3000, this, &macOsGrabber::start); - } + if (stopNow) + { + uninit(); + QTimer::singleShot(3000, this, &macOsGrabber::start); + } + #endif } diff --git a/sources/hyperhdr/CMakeLists.txt b/sources/hyperhdr/CMakeLists.txt index dffb4bd98..500c562a5 100644 --- a/sources/hyperhdr/CMakeLists.txt +++ b/sources/hyperhdr/CMakeLists.txt @@ -111,6 +111,9 @@ if (ENABLE_MAC_SYSTEM) MACOS-grabber "-framework CoreGraphics" "-framework Foundation") + if (MACOS_SCK) + target_link_libraries(hyperhdr "${MACOS_SCK}") + endif(MACOS_SCK) endif() if (ENABLE_AVF) diff --git a/sources/sound-capture/macos/CMakeLists.txt b/sources/sound-capture/macos/CMakeLists.txt index c7c06dab7..b217158a6 100644 --- a/sources/sound-capture/macos/CMakeLists.txt +++ b/sources/sound-capture/macos/CMakeLists.txt @@ -11,6 +11,10 @@ target_link_libraries(sound-capture-macos Qt${Qt_VERSION}::Network ) +if (${MACOS_MAJOR_VERSION} GREATER_EQUAL "14") + target_compile_definitions(sound-capture-macos PUBLIC MACOS_VERSION_14_UP) +endif() + if(USE_PRECOMPILED_HEADERS AND COMMAND target_precompile_headers AND (NOT APPLE)) target_precompile_headers(sound-capture-macos REUSE_FROM precompiled_hyperhdr_headers) endif() diff --git a/sources/sound-capture/macos/SoundCaptureMacOS.mm b/sources/sound-capture/macos/SoundCaptureMacOS.mm index 7a71f7574..1ae307ffc 100644 --- a/sources/sound-capture/macos/SoundCaptureMacOS.mm +++ b/sources/sound-capture/macos/SoundCaptureMacOS.mm @@ -87,7 +87,11 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB void SoundCaptureMacOS::listDevices() { AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] + #ifndef MACOS_VERSION_14_UP + discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] + #else + discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeMicrophone] + #endif mediaType:AVMediaTypeAudio position:AVCaptureDevicePositionUnspecified]; @@ -144,7 +148,11 @@ - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleB _soundBufferIndex = 0; for (AVCaptureDevice* device in [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] + #ifndef MACOS_VERSION_14_UP + discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInMicrophone] + #else + discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeMicrophone] + #endif mediaType:AVMediaTypeAudio position:AVCaptureDevicePositionUnspecified].devices) {