Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions cmake/compile_definitions/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ list(APPEND SUNSHINE_EXTERNAL_LIBRARIES
${CORE_MEDIA_LIBRARY}
${CORE_VIDEO_LIBRARY}
${FOUNDATION_LIBRARY}
${SCREEN_CAPTURE_KIT_LIBRARY}
${VIDEO_TOOLBOX_LIBRARY})

set(APPLE_PLIST_TEMPLATE "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/build/Info.plist.in")
Expand All @@ -45,8 +46,6 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_audio.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_img_t.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/av_video.m"
"${CMAKE_SOURCE_DIR}/src/platform/macos/display.mm"
"${CMAKE_SOURCE_DIR}/src/platform/macos/input.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/microphone.mm"
Expand All @@ -55,6 +54,16 @@ set(PLATFORM_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/platform/macos/nv12_zero_device.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/nv12_zero_device.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/publish.cpp"
"${CMAKE_SOURCE_DIR}/src/platform/macos/sc_video.h"
"${CMAKE_SOURCE_DIR}/src/platform/macos/sc_video.m"
"${CMAKE_SOURCE_DIR}/third-party/TPCircularBuffer/TPCircularBuffer.c"
"${CMAKE_SOURCE_DIR}/third-party/TPCircularBuffer/TPCircularBuffer.h"
${APPLE_PLIST_FILE})

# sc_video.m is written against ARC for clarity (SCK APIs are async/
# block-heavy and benefit from ARC). The rest of the macOS Obj-C
# sources remain MRC; objects flowing across the boundary follow the
# standard +1-retain alloc/init convention so both modes interoperate.
set_source_files_properties(
"${CMAKE_SOURCE_DIR}/src/platform/macos/sc_video.m"
PROPERTIES COMPILE_FLAGS "-fobjc-arc")
8 changes: 8 additions & 0 deletions cmake/dependencies/macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia)
FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo)
FIND_LIBRARY(FOUNDATION_LIBRARY Foundation)
FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox)
# ScreenCaptureKit is the modern (macOS 12.3+) replacement for the
# deprecated AVCaptureScreenInput-based capture path. Sunshine's
# sc_video.{h,m} is unconditionally compiled into the macOS target;
# fail configure with a clear message rather than failing the build
# later on header lookup when the SDK doesn't ship the framework
# (e.g., when building with an Xcode older than 13.3 / SDK older than
# 12.3, which dropped out of routine compatibility long ago).
FIND_LIBRARY(SCREEN_CAPTURE_KIT_LIBRARY ScreenCaptureKit REQUIRED)
Comment on lines +14 to +20

if(SUNSHINE_ENABLE_TRAY)
FIND_LIBRARY(COCOA Cocoa REQUIRED)
Expand Down
41 changes: 0 additions & 41 deletions src/platform/macos/av_video.h

This file was deleted.

146 changes: 0 additions & 146 deletions src/platform/macos/av_video.m

This file was deleted.

28 changes: 20 additions & 8 deletions src/platform/macos/display.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
#include "src/logging.h"
#include "src/platform/common.h"
#include "src/platform/macos/av_img_t.h"
#include "src/platform/macos/av_video.h"
#include "src/platform/macos/misc.h"
#include "src/platform/macos/nv12_zero_device.h"
#include "src/platform/macos/sc_video.h"

// Avoid conflict between AVFoundation and libavutil both defining AVMediaType
#define AVMediaType AVMediaType_FFmpeg
Expand All @@ -22,7 +22,7 @@
using namespace std::literals;

struct av_display_t: public display_t {
AVVideo *av_capture {};
SCVideo *av_capture {};
CGDirectDisplayID display_id {};

~av_display_t() override {
Expand Down Expand Up @@ -86,7 +86,7 @@ capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const
} else if (pix_fmt == pix_fmt_e::nv12 || pix_fmt == pix_fmt_e::p010) {
auto device = std::make_unique<nv12_zero_device>();

device->init(static_cast<void *>(av_capture), pix_fmt, setResolution, setPixelFormat);
device->init((void *) av_capture, pix_fmt, setResolution, setPixelFormat);

return device;
} else {
Expand Down Expand Up @@ -143,11 +143,11 @@ int dummy_img(img_t *img) override {
* height --> the intended capture height
*/
static void setResolution(void *display, int width, int height) {
[static_cast<AVVideo *>(display) setFrameWidth:width frameHeight:height];
[(SCVideo *) display setFrameWidth:width frameHeight:height];
}

static void setPixelFormat(void *display, OSType pixelFormat) {
static_cast<AVVideo *>(display).pixelFormat = pixelFormat;
((SCVideo *) display).pixelFormat = pixelFormat;
}
};

Expand All @@ -163,7 +163,7 @@ static void setPixelFormat(void *display, OSType pixelFormat) {
display->display_id = CGMainDisplayID();

// Print all displays available with it's name and id
auto display_array = [AVVideo displayNames];
auto display_array = [SCVideo displayNames];
BOOST_LOG(info) << "Detecting displays"sv;
for (NSDictionary *item in display_array) {
NSNumber *display_id = item[@"id"];
Expand All @@ -177,7 +177,19 @@ static void setPixelFormat(void *display, OSType pixelFormat) {
}
BOOST_LOG(info) << "Configuring selected display ("sv << display->display_id << ") to stream"sv;

display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate];
// ScreenCaptureKit is the only capture backend Sunshine ships on macOS;
// the deployment target (14.2) is well above SCK's minimum (12.3) so
// there is no @available branch and no legacy AVCaptureScreenInput
// fallback to maintain.
//
// hdrAllowed reflects the negotiated `enable_hdr` for this session
// (rtsp.cpp maps `x-nv-video[0].dynamicRangeMode` into config.dynamicRange).
// SCK uses this together with the chosen pixel format depth to decide
// whether to flip captureDynamicRange to HDRLocalDisplay; neither
// condition alone is sufficient. See sc_video.m::applyDynamicRangeForPixelFormat:.
const BOOL hdr_allowed = config.dynamicRange ? YES : NO;
BOOST_LOG(info) << "Using ScreenCaptureKit capture backend (HDR "sv << (hdr_allowed ? "allowed" : "blocked") << ")"sv;
display->av_capture = [[SCVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate hdrAllowed:hdr_allowed];

if (!display->av_capture) {
BOOST_LOG(error) << "Video setup failed."sv;
Expand All @@ -196,7 +208,7 @@ static void setPixelFormat(void *display, OSType pixelFormat) {
std::vector<std::string> display_names(mem_type_e hwdevice_type) {
__block std::vector<std::string> display_names;

auto display_array = [AVVideo displayNames];
auto display_array = [SCVideo displayNames];

display_names.reserve([display_array count]);
[display_array enumerateObjectsUsingBlock:^(NSDictionary *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
Expand Down
47 changes: 47 additions & 0 deletions src/platform/macos/sc_video.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @file src/platform/macos/sc_video.h
* @brief Declarations for ScreenCaptureKit-based video capture on macOS.
*
* SCVideo is now Sunshine's only macOS capture backend. The deployment
* target (MACOSX_DEPLOYMENT_TARGET=14.2) is well above the macOS 12.3
* minimum where ScreenCaptureKit became available, so the legacy
* AVCaptureScreenInput-based AVVideo path has been removed entirely.
*/
#pragma once

#import <AppKit/AppKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>

// Block signature used to deliver captured sample buffers back to the
// platform-agnostic capture loop. Returning NO from the block stops
// further deliveries on this capture session.
typedef bool (^FrameCallbackBlock)(CMSampleBufferRef);

@interface SCVideo : NSObject

@property (nonatomic, assign) CGDirectDisplayID displayID;
@property (nonatomic, assign) CMTime minFrameDuration;
@property (nonatomic, assign) OSType pixelFormat;
@property (nonatomic, assign) int frameWidth;
@property (nonatomic, assign) int frameHeight;

// YES iff the negotiated streaming session enabled HDR (Moonlight's
// hdrMode flag). Required (in combination with a 10-bit pixel format)
// before SCK is allowed to flip captureDynamicRange to HDRLocalDisplay
// on macOS 14+. Defaults to NO; the SDR capture path is always safe.
@property (nonatomic, assign) BOOL hdrAllowed;

- (instancetype)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate;
- (instancetype)initWithDisplay:(CGDirectDisplayID)displayID frameRate:(int)frameRate hdrAllowed:(BOOL)hdrAllowed;

- (void)setFrameWidth:(int)frameWidth frameHeight:(int)frameHeight;
- (dispatch_semaphore_t)capture:(FrameCallbackBlock)frameCallback;

// Enumerate the currently-active CGDisplays as an array of dictionaries
// with keys @"id" (NSNumber, the CGDirectDisplayID), @"name" (NSString,
// the numeric id as a string for legacy callers), and @"displayName"
// (NSString, the user-facing name from NSScreen.localizedName).
+ (NSArray<NSDictionary *> *)displayNames;

@end
Loading