macOS录屏AVCaptureScreenInput
macOS录屏AVCaptureScreenInput

macOS录屏AVCaptureScreenInput

AVCaptureScreenInput 是 Apple 提供的 macOS 平台上的屏幕采集输入源类,属于 AVFoundation 框架的一部分,主要用于实现“录屏”或“屏幕捕捉”功能。

在使用时,需要引入AVFoundation框架:

import AVFoundation

基本定义

class AVCaptureScreenInput : AVCaptureInput

它是 AVCaptureInput 的子类,可以把「屏幕」当作一个输入源添加到 AVCaptureSession 中,从而实现录制屏幕、获取屏幕帧图像等功能。

初始化方法:

init(displayID: CGDirectDisplayID)

例如,传入一个主屏幕的CGDirectDisplayID(屏幕的唯一标识符):

let input = AVCaptureScreenInput(displayID: CGMainDisplayID())

AVCaptureScreenInput常用属性

1、cropRect:CGRect类型,裁剪区域,只捕捉屏幕的某一部分。

2、scaleFactor:CGFloat类型,缩放因子,默认是 1.0。设置为 0.5 可将输出画面缩小一半。

screenInput.minFrameDuration = 0.5

3、minFrameDuration:CMTime类型,设置最小帧间隔,控制帧率,例如设置为 1/30 表示每秒 30 帧。

screenInput.minFrameDuration = CMTime(value: 1, timescale: 30)

4、capturesCursor:Bool类型,是否捕捉鼠标光标(默认 true)。

screenInput.capturesCursor = true

5、capturesMouseClicks:Bool类型,是否捕捉鼠标点击时的动画(默认 true)。

screenInput.capturesMouseClicks = true

6、removesDuplicateFrames:Bool类型,是否丢弃重复的帧,节省资源,默认 true。

screenInput.removesDuplicateFrames = false

7、displayID:CGDirectDisplayID类型,当前捕捉的显示器 ID。

AVCaptureVideoDataOutput常用属性和方法

1、videoSettings:[String: Any]?类型,设置输出格式,如 kCVPixelBufferPixelFormatTypeKey。默认是 YUV 格式。

output.videoSettings = [
    kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
]

2、sampleBufferCallbackQueue:DispatchQueue?类型,委托回调的队列,使用 setSampleBufferDelegate 设置。

3、alwaysDiscardsLateVideoFrames:Bool类型,如果为 true,会丢弃延迟帧,保证实时性(默认 true)。

output.setSampleBufferDelegate(outClass, queue: DispatchQueue(label: "VideoOutput"))

4、setSampleBufferDelegate(_:queue:):设置代理,用于接收视频帧数据。

实现于 AVCaptureVideoDataOutputSampleBufferDelegate:

func captureOutput(_ output: AVCaptureOutput,
    didOutput sampleBuffer: CMSampleBuffer,
    from connection: AVCaptureConnection)

每一帧视频都会调用此方法。

使用示例

import AVFoundation

let session = AVCaptureSession()
guard let screenInput = AVCaptureScreenInput(displayID: CGMainDisplayID()) else { return }

if session.canAddInput(screenInput) {
    session.addInput(screenInput)
}

let output = AVCaptureVideoDataOutput()
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "VideoOutput"))

if session.canAddOutput(output) {
    session.addOutput(output)
}

session.startRunning()

代码详解

1、创建捕获会话

创建一个“捕捉会话(session)”,它是整个视频采集的核心容器。

let session = AVCaptureSession()

所有的输入(屏幕、摄像头等)和输出(视频文件、图像处理、展示等)都必须加入到 session 中。

2、创建屏幕输入源

创建一个“屏幕输入源”,也就是指定哪个屏幕作为采集目标。

guard let screenInput = AVCaptureScreenInput(displayID: CGMainDisplayID()) else { return }

CGMainDisplayID() 表示主屏幕(即当前主要在用的屏幕)。

AVCaptureScreenInput 就是将屏幕图像作为视频输入源,返回可选的AVCaptureScreenInput类型,如果没有可用屏幕或该屏幕无法用于捕获,则返回nil。

3、判断添加输入源

if session.canAddInput(screenInput) {
    session.addInput(screenInput)
}

判断是否能添加输入源,然后将其添加到会话中。

4、创建视频数据输出对象

let output = AVCaptureVideoDataOutput()

用于 “处理每一帧捕捉到的视频数据”(比如用于保存为文件、转为图片等)。

5、设置缓冲区代理

output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "VideoOutput"))

设置代理,用来接收每一帧图像(每一帧是 CMSampleBuffer 类型),并且指定处理的线程队列。

这里可以使用self或单独的代理对象:

extension OutClass: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, 
                       didOutput sampleBuffer: CMSampleBuffer, 
                       from connection: AVCaptureConnection) {
        // 每一帧视频都会调用这个方法
    }
}

6、检查输出对象

if session.canAddOutput(output) {
    session.addOutput(output)
}

判断是否能添加输出对象,并加入会话中。

7、启动整个采集流程

session.startRunning()

如果需要停止采集流程,需要调用stopRunning方法:

session.stopRunning()

因此,在实际的使用中,推荐将AVCaptureSession作为类的属性保存,当需要停止采集视频时,调用stopRunning()停止采集。

class YourController: NSObject {
    
    var session: AVCaptureSession?
    var outClass: OutClass?
    
    @MainActor
    @objc func screenshot() {
        
        let session = AVCaptureSession()
        guard let screenInput = AVCaptureScreenInput(displayID: CGMainDisplayID()) else { return }

        if session.canAddInput(screenInput) {
            session.addInput(screenInput)
        }

        let output = AVCaptureVideoDataOutput()
        let outClass = OutClass()
        output.setSampleBufferDelegate(outClass, queue: DispatchQueue(label: "VideoOutput"))

        if session.canAddOutput(output) {
            session.addOutput(output)
        }

        self.session = session
        self.outClass = outClass

        session.startRunning()
    }

    @objc func stopCapture() {
        print("停止屏幕采集")
        session?.stopRunning()
        session = nil
        outClass = nil
    }
}

class OutClass: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput,
                       didOutput sampleBuffer: CMSampleBuffer,
                       from connection: AVCaptureConnection) {
        // 每一帧视频都会调用这个方法
    }
}

调用方式:

let controller = YourController()
controller.screenshot()

// 稍后想停止时
controller.stopCapture()

注意事项

1、使用 AVCaptureScreenInput 时,系统需要屏幕录制权限,否则Xcode会提示:

CMIO_Unit_Input_ASC.mm:2047:Start Error: _Start returned false
CMIO_Graph.cpp:5973:Start start graph ... returned error 2003329396 (what)
Missing device-camera entitlement

这表示macOS在底层尝试初始化屏幕捕捉设备失败的结果,无法捕捉屏幕画面。

2、需在 Info.plist 中添加隐私说明:

<key>NSCameraUsageDescription</key>
<string>需要使用屏幕进行录制</string>

在 Xcode16 中,不需要添加这一配置,仍然可以实现屏幕录制。

3、macOS 会自动弹窗询问是否授权(首次使用时),也可以引导用户打开系统设置 > 安全性与隐私 > 录屏与系统录音。

4、在实际使用中,AVCaptureSession和AVCaptureVideoDataOutputSampleBufferDelegate代理类,必须作为类中的属性进行引用

class StatusBarController:ObservableObject {
    var session: AVCaptureSession?
    var outClass: OutClass?

如果不使用强引用的话,只是在局部环境中创建AVCaptureSession对象,是无法录制屏幕的,甚至连AVCaptureVideoDataOutputSampleBufferDelegate代理方法都无法执行。

总结

AVCaptureScreenInput使用简单,可以直接录制屏幕,支持自定义帧数、缩放等参数,支持鼠标捕捉。

但是仅适用于macOS,不支持iOS,无法指定App窗口,仅支持屏幕级捕捉。

AVCaptureScreenInput和ScreenCaptureKit的区别

AVCaptureScreenInput在macOS 10.7+版本引入,支持整个显示屏的屏幕录制,支持capturesCursor、capturesMouseClicks等鼠标捕获,可以捕获整个屏幕或裁剪区域,适合简单录屏、屏幕录制工具。

ScreenCaptureKit在macOS 13+版本引入,支持单个App窗口、区域和显示器的屏幕录制,支持光标捕获,可以精确捕获App、窗口、层、组合内容,适合专业录屏、直播、远程协助、虚拟会议等场景。

快速实现屏幕录制,或者需要兼容macOS 12及以前的版本,可以使用AVCaptureScreenInput。

捕获App、窗口、图层、跨显示器,获得更高性能和现代权限控制,支持macOS 13+,推荐使用ScreenCaptureKit。

相关文章

1、macOS屏幕捕捉ScreenCaptureKit:https://fangjunyu.com/2025/07/23/macos%e5%b1%8f%e5%b9%95%e6%8d%95%e6%8d%89screencapturekit/

扩展知识

AVCaptureVideoDataOutputSampleBufferDelegate输出信息

class OutClass: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput,
                       didOutput sampleBuffer: CMSampleBuffer,
                       from connection: AVCaptureConnection) {
        // 每一帧视频都会调用这个方法
        print("采集视频中")
        print("output:\(output)")
        print("sampleBuffer:\(sampleBuffer)")
        print("connection:\(connection)")
    }
}

运行时,Xcode输出:

采集视频中
output:<AVCaptureVideoDataOutput_Tundra: 0x6000023b9a80>
sampleBuffer:CMSampleBuffer 0x1576281d0 retainCount: 3 allocator: 0x1f6b40120
    invalid = NO
    dataReady = YES
    makeDataReadyCallback = 0x0
    makeDataReadyRefcon = 0x0
    buffer-level attachments:
        com.apple.cmio.buffer_attachment.discontinuity_flags(P) = 0
        com.apple.cmio.buffer_attachment.sequence_number(P) = 137
        com.apple.cmio.buffer_attachment.hosttime(P) = 914049517666667
        com.apple.cmio.buffer_attachment.VTCompressionPriority(P) = 60
        com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers(P) = {
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_frame_rect" =     {
        Height = 46;
        Width = 34;
        X = 337;
        Y = 1706;
    };
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_is_drawn_in_frame_buffer" = 0;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_position_x" = "172.625";
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_position_y" = "84.01953125";
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_reference" = "<NSCursor: 0x600002c5eee0>";
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_scale" = 1;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_seed" = 738689;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.cursor_visible" = 1;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.key_modifiers" = 0;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.key_modifiers_event" = 0;
    "com.apple.cmio.buffer_attachment.mouse_and_keyboard_modifiers.mouse_button_state" = 0;
}
    formatDescription = <CMVideoFormatDescription 0x600002c23d20 [0x1f6b40120]> {
    mediaType:'vide' 
    mediaSubType:'2vuy' 
    mediaSpecific: {
        codecType: '2vuy'       dimensions: 2940 x 1912 
    } 
    extensions: {{
    CVBytesPerRow = 5888;
    CVImageBufferChromaLocationBottomField = Center;
    CVImageBufferChromaLocationTopField = Center;
    CVImageBufferColorPrimaries = "ITU_R_709_2";
    CVImageBufferGammaLevel = "1.960999965667725";
    CVImageBufferTransferFunction = "ITU_R_709_2";
    CVImageBufferYCbCrMatrix = "ITU_R_709_2";
    Version = 2;
    "com.apple.cmio.format_extension.video.only_has_i_frames" = 1;
}}
}
    sbufToTrackReadiness = 0x0
    numSamples = 1
    outputPTS = {5484297106/6000 = 914049.518, rounded}(based on cachedOutputPresentationTimeStamp)
    sampleTimingArray[1] = {
        {PTS = {5484297106/6000 = 914049.518, rounded}, DTS = {INVALID}, duration = {400/6000 = 0.067, rounded}},
    }
    imageBuffer = 0x600001260140
   

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

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

发表回复

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