macOS屏幕捕捉ScreenCaptureKit
macOS屏幕捕捉ScreenCaptureKit

macOS屏幕捕捉ScreenCaptureKit

ScreenCaptureKit 是 Apple 在 macOS 13(Ventura)及以上系统中引入的高性能屏幕捕捉框架,用于采集屏幕内容(窗口、显示器、应用),适用于开发录屏、远程桌面、虚拟摄像头、游戏直播等应用。

相比传统的 CGDisplayStream 和 AVCaptureScreenInput,ScreenCaptureKit 更现代、更高效、更灵活。

基本特性

1、高性能:使用 Metal 加速的帧处理,适合高帧率场景(如游戏录制);

2、低延迟:实时传输屏幕图像(适合直播或投屏);

3、精细选择:可精确选择捕捉全部屏幕、单个窗口、多个窗口、单个应用的所有窗口;

4、隐私安全:支持 macOS 系统的权限机制(如录屏权限);

5、支持窗口透明度:可选是否保留窗口的圆角、阴影、透明效果;

6、支持 Metal 和 SwiftUI:捕获的图像可直接传入 Metal 或 SwiftUI 图层中渲染。

基本用法

import ScreenCaptureKit

// 1. 创建内容过滤器(要捕获什么)
let filter = SCContentFilter(
    display: SCDisplay,              // SCDisplay实例
    excludingApplications: [], // 可排除 App
    exceptingWindows: []       // 可排除窗口
)

// 2. 配置捕获参数
let config = SCStreamConfiguration()
config.showsCursor = true
config.capturesAudio = false
config.minimumFrameInterval = CMTime(value: 1, timescale: 60) // 60 FPS

// 3. 创建捕获流
let stream = try await SCStream(filter: filter, configuration: config, delegate: yourDelegate)
try stream.addStreamOutput(output, type: .screen, sampleHandlerQueue: .main)
try await stream.startCapture()

代码解析

1、创建内容过滤器:SCContentFilter

let filter = SCContentFilter(
    display: SCDisplay,	// 
    excludingApplications: [],
    exceptingWindows: []
)

1、display: SCDisplay

需要传入一个 SCDisplay 对象,指定某个屏幕。

可以通过SCShareableContent获取全部显示器(主显示器 + 外接显示器):

let content = try? await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true)
guard let displays = content?.displays else { return } // [SCDisplay]
for display in displays {
    print("display:\(display)") // display:<SCDisplay: 0x600001dc5180>
}

将返回的SCDisplay对象传入display参数。

2、excludingApplications: []

用于指定不想被捕获的 App,可以排除防止录自己。

类型为 [SCApplication],代表一组应用。

可以通过 SCShareableContent 获取系统中正在运行的可捕获应用:

let apps = content.applications // [SCApplication]

常见用途是排除自己这个应用,避免录到自己的浮窗等内容:

let myApp = content.applications.first { $0.bundleIdentifier == Bundle.main.bundleIdentifier }

3、exceptingWindows: []

排除特定窗口的显示内容,例如隐私敏感窗口或不想录制的弹窗。

类型为 [SCWindow],可用于过滤掉某些不希望录入的特定窗口。

可通过 SCShareableContent 获取系统所有可捕获窗口:

let windows = content.windows // [SCWindow]

2、配置捕获参数:SCStreamConfiguration

let config = SCStreamConfiguration()
config.showsCursor = true   // 录制光标
config.capturesAudio = false  // 不录制麦克风或系统音
config.minimumFrameInterval = CMTime(value: 1, timescale: 60) // 60 FPS

1、showsCursor:Bool类型,是否录制光标。截图工具一般设为 true,方便展示用户操作。

2、capturesAudio:Bool类型,是否同时录制麦克风或系统音。截图工具多数设为 false。

3、minimumFrameInterval:CMTime类型,帧率控制。上面配置为 1/60 秒,即 60FPS。

4、pixelFormat (可选):OSType类型,输出的图像格式,比如 kCVPixelFormatType_32BGRA。

5、width, height (可选):Int类型,期望的输出分辨率(可以不设,自动与屏幕分辨率匹配)。

3、创建并启动捕获流:SCStream

let stream = try await SCStream(filter: filter, configuration: config, delegate: yourDelegate)
try stream.addStreamOutput(output, type: .screen, sampleHandlerQueue: .main)
try await stream.startCapture()

1、SCStream:表示一个屏幕捕获流。

2、filter:指定要捕获哪些内容(屏幕、窗口、应用排除)。

3、configuration:帧率、是否录光标等设置。

4、delegate:必须实现 SCStreamDelegate 协议,用于响应流状态变化和错误处理。

delegate 采用的是协议 SCStreamDelegate,目前该协议只有一个方法:

protocol SCStreamDelegate: AnyObject {
    func stream(_ stream: SCStream, didStopWithError error: Error)
}

用于监听流是否因错误而停止。

delegate示例:

var streamDelegate: MyStreamDelegate?

class MyStreamDelegate: NSObject, SCStreamDelegate {
    func stream(_ stream: SCStream, didStopWithError error: Error) {
        print("捕获流停止,原因:\(error.localizedDescription)")
    }
}

let myDelegate = MyStreamDelegate()
streamDelegate = myDelegate      // 保留引用

let stream = try await SCStream(filter: filter, configuration: config, delegate: myDelegate)

注意:在实际应用

5、addStreamOutput(…)

stream.addStreamOutput(output, type: .screen, sampleHandlerQueue: .main)

1)output:需遵守 SCStreamOutput 协议的对象,接收图像帧(CMSampleBuffer)。

output采用的是协议 SCStreamDelegate,目前该协议也只有一个方法:

public protocol SCStreamOutput : NSObjectProtocol {
    optional func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType)
}

在这个方法中处理捕获到的视频帧或音频帧,比如渲染、保存成图片、录制成视频等。

output示例:

var streamOutput: MyStreamOutput?

class MyStreamDelegate: NSObject, SCStreamDelegate {
    func stream(_ stream: SCStream, didStopWithError error: Error) {
        print("捕获流停止,原因:\(error.localizedDescription)")
    }
}

let output = MyStreamOutput()
streamOutput = output      // 保留引用

try? stream?.addStreamOutput(output, type: .screen, sampleHandlerQueue: .main)

获取屏幕帧后,可以将它编码为 MP4 或写入文件,通常需要用 AVAssetWriter 去编码保存:

// 创建 AVAssetWriter + AVAssetWriterInput
// 在 didOutputSampleBuffer 中逐帧写入

2)type:指定要捕获的数据类型,有两个可选值(SCStreamOutputType):

.screen:捕获屏幕图像(视频帧)数据,主要用于录屏或图像处理;

.audio:捕获音频数据(系统声音或麦克风);

使用 .audio 时需要配置 SCStreamConfiguration 的音频相关参数,否则不会有音频输出。

3)sampleHandlerQueue:SCStream 异步回调的队列,通常用于处理输出的数据(图像帧、音频帧)。

.main:回调在主线程(UI 操作时使用);

DispatchQueue(label: “my.queue”):自定义并发或串行队列,推荐用于后台处理,如图像编码或写入磁盘等。

6、startCapture()

try await stream.startCapture()

用于启动屏幕捕获过程,一次捕获中只能调用一次,重复调用需先 .stopCapture()。

应用场景

1、macOS 屏幕录像 App(高帧率);

2、桌面直播工具(OBS、Twitch 客户端等);

3、屏幕协同工具(远程演示、协同办公);

4、开发虚拟摄像头驱动(将桌面作为摄像源);

5、更高质量截图工具(实时显示窗口动态)。

注意事项

1、ScreenCaptureKit需要macOS 13 Ventura 及以上;

2、屏幕录制权限(Screen Recording),用户必须在系统设置中开启。

3、ScreenCaptureKit没有保存路径,需要在实现SCStreamOutput协议的类中获取CMSampleBuffer(视频帧),编码为 MP4 或写入文件保存。

4、在实际使用中,需要保留SCStream示例,当需要结束录制的时候,调用:

try await stream.stopCapture()

示例:

var stream: SCStream?

func stopRecording() async {
    do {
        try await stream?.stopCapture()
        print("录制已停止")
    } catch {
        print("停止录制出错:\(error)")
    }
}

5、Delegate和Output协议的对象,在实际应用中不能保存为局部变量,当当 Task 块执行完毕之后,它们会立即释放销毁(即便 stream 还在运行中),导致 SCStream 在回调中访问已释放的 delegate/output,从而触发 EXC_BAD_ACCESS。

应该将它们保存为全局变量,保证它们的生命周期与 stream 一致。

var stream: SCStream?
var streamDelegate: MyStreamDelegate?
var streamOutput: MyStreamOutput?

Task {
    let myDelegate = MyStreamDelegate()
    let output = MyStreamOutput()
    streamDelegate = myDelegate      // 保留引用
    streamOutput = output   // 保留引用

    stream = SCStream(filter: filter, configuration: config, delegate: myDelegate)
    try? stream?.addStreamOutput(output, type: .screen, sampleHandlerQueue: .main)
    try? await stream?.startCapture()
}

总结

ScreenCaptureKit 是 Apple 为 macOS 提供的最现代、最高效的屏幕捕捉框架,适合新一代截图、录屏、直播类 App 的开发。

如果在使用SCStream的过程中,发生EXC_BAD_ACCESS崩溃,可能是因为某些对象的生命周期或线程访问不安全,违反了 ScreenCaptureKit 的线程要求。

SCStream 及其相关的 API(如 addStreamOutput, startCapture, stopCapture)必须在主线程上调用,并且对象本身也应在主线程创建、使用、销毁。

解决方案为,使用Task包裹async调用:

Task { @MainActor in 
    try await ...
}

或者使用@MainAcotr修饰方法

@MainActor
@objc func screenshot() {
    // 代码
}

在实际调用时,应用会在状态栏显示录屏图标:

Xcode输出屏幕帧数据:

获取到屏幕帧数据:<CVPixelBuffer 0x600000c6f8e0 width=1920 height=1080 pixelFormat=420v iosurface=0x600003f48370 surfaceid=82 planes=2>
<Plane 0 width=1920 height=1080 bytesPerRow=1920>
<Plane 1 width=960 height=540 bytesPerRow=1920>
<attributes={
    ExtendedPixelsBottom = 0;
    ExtendedPixelsLeft = 0;
    ExtendedPixelsRight = 0;
    ExtendedPixelsTop = 0;
    PixelFormatDescription =     {
        BitsPerComponent = 8;
        ComponentRange = VideoRange;
        ContainsAlpha = 0;
        ContainsGrayscale = 0;
        ContainsRGB = 0;
        ContainsYCbCr = 1;
        FillExtendedPixelsCallback = {length = 24, bytes = 0x0000000000000000a0c6f18f010000000000000000000000};
        IOSurfaceCoreAnimationCompatibility = 1;
        PixelFormat = 875704438;
        Planes =         (
                        {
                BitsPerBlock = 8;
                BlackBlock = {length = 1, bytes = 0x10};
            },
                        {
                BitsPerBlock = 16;
                BlackBlock = {length = 2, bytes = 0x8080};
                HorizontalSubsampling = 2;
                VerticalSubsampling = 2;
            }
        );
    };
} propagatedAttachments={
    CGColorSpace = "<CGColorSpace 0x60000194ca20> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; \U5f69\U8272LCD)";
    CVImageBufferChromaLocationTopField = Left;
    CVImageBufferChromaSubsampling = "4:2:0";
    CVImageBufferColorPrimaries = "ITU_R_709_2";
    CVImageBufferTransferFunction = "ITU_R_709_2";
    CVImageBufferYCbCrMatrix = "ITU_R_709_2";
} nonPropagatedAttachments={
}>

相关文章

1、ScreenCaptureKit:https://developer.apple.com/documentation/screencapturekit

2、macOS可捕获内容SCShareableContent:https://fangjunyu.com/2025/07/23/macos%e5%8f%af%e6%8d%95%e8%8e%b7%e5%86%85%e5%ae%b9scshareablecontent/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注