Capture and stream apps on the Mac with ReplayKit
Graphics & Games 进阶 20m

在 Mac 上使用 ReplayKit 录屏和直播

Capture and stream apps on the Mac with ReplayKit

2020年6月26日

在 Apple 官方观看视频

一句话判断

如果你的 Mac 应用需要录屏或直播功能,ReplayKit 现在可以在 macOS 上用了——而且是系统级的、低性能开销的实现。

这场 Session 讲了什么

这场 Session 介绍了 ReplayKit 在 macOS 上的首次完整支持。ReplayKit 最初是 iOS 的录屏框架,2020 年终于来到了 macOS,让 Mac 应用也能集成录制和直播功能。

演讲者首先介绍了 macOS 上 ReplayKit 的三种录制模式:App Recording(只录制应用自身的窗口)、Screen Recording(录制整个屏幕,包括其他应用)和 Broadcast(直播推流到服务器)。每种模式有不同的权限要求和隐私考量。

App Recording 是最简单的模式,只需要用户在应用内确认即可。Screen Recording 需要用户在系统偏好设置中授予权限。Broadcast 则需要一个 Broadcast Upload Extension 来处理视频帧的编码和推流。

Session 的核心部分讲解了如何配置录制参数(分辨率、帧率、音频源),以及如何使用 RPScreenRecorder 的 API 来控制录制的开始/暂停/恢复/停止。演讲者还介绍了录制后的编辑界面——RPPreviewViewController 提供了内置的视频裁剪和分享功能。

最后讨论了性能考量。ReplayKit 使用系统的硬件编码器,CPU 开销很低。但录制会占用 GPU 资源,对于已经 GPU 密集的应用(如游戏),可能会影响帧率。

值得深挖的点

  • Broadcast Upload Extension 的架构:直播推流需要创建一个 App Extension,ReplayKit 会将每一帧的视频和音频数据发送到这个 Extension 中处理。Extension 负责编码和推流。这种架构将录制逻辑和应用逻辑隔离,即使应用崩溃,直播流也不会中断。
  • 隐私与权限模型:macOS 的屏幕录制权限在系统偏好设置 > 安全性与隐私 > 屏幕录制中管理。如果用户拒绝了这个权限,你的应用无法录制屏幕。Apple 要求所有使用 Screen Recording 的应用必须在 App Store 审核时说明录制用途。

代码片段

// 开始录制应用窗口
import ReplayKit

class RecordingManager: NSObject, RPScreenRecorderDelegate {
    let recorder = RPScreenRecorder.shared()

    func startRecording() {
        // 检查是否可用
        guard recorder.isAvailable else {
            print("ReplayKit 不可用")
            return
        }

        recorder.isMicrophoneEnabled = true  // 启用麦克风
        recorder.isCameraEnabled = false      // 不需要摄像头

        recorder.startRecording(
            handler: { error in
                if let error = error {
                    print("录制启动失败: \(error)")
                } else {
                    print("录制已开始")
                }
            }
        )
    }

    func stopRecording() {
        recorder.stopRecording { [weak self] previewVC, error in
            if let previewVC = previewVC {
                previewVC.previewControllerDelegate = self
                self?.present(previewVC, animated: true)
            }
        }
    }
}
// 处理录制预览的回调
extension RecordingManager: RPPreviewViewControllerDelegate {
    func previewController(
        _ previewController: RPPreviewViewController,
        didFinishWithActivityTypes activityTypes: Set<RPActivityType>
    ) {
        if activityTypes.contains(.saveToCameraRoll) {
            print("视频已保存到相册")
        }
        if activityTypes.contains(.share) {
            print("用户选择了分享")
        }
        previewController.dismiss(animated: true)
    }
}
// 直播推流的 Broadcast Setup
// 在 Broadcast Upload Extension 中处理视频帧
class SampleHandler: RPSampleBufferQueueHandler {
    override func processSampleBuffer(
        _ sampleBuffer: CMSampleBuffer,
        with sampleBufferType: RPSampleBufferType
    ) {
        switch sampleBufferType {
        case .video:
            // 处理视频帧
            if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
                encodeAndStreamVideoFrame(pixelBuffer)
            }
        case .audioMic:
            // 处理麦克风音频
            encodeAndStreamAudio(sampleBuffer)
        case .audioApp:
            // 处理应用音频
            encodeAndStreamAppAudio(sampleBuffer)
        @unknown default:
            break
        }
    }

    func encodeAndStreamVideoFrame(_ frame: CVPixelBuffer) {
        // 使用 VideoToolbox 编码为 H.264
        // 然后通过 RTMP 或其他协议推流
    }
}

最佳实践

  • 录制前检查 RPScreenRecorder.shared().isAvailable,某些设备或场景下可能不可用
  • 使用 App Recording 模式而非 Screen Recording,除非你确实需要录制其他应用
  • 录制时在 UI 上提供清晰的指示(如红色录制按钮),让用户知道正在录制
  • 处理录制中断(如系统内存不足强制停止)和错误恢复
  • GPU 密集型应用考虑降低录制分辨率以减少性能影响

还有什么值得关注

  • macOS 的 ReplayKit 后来在 Monterey 中增加了更多功能,包括窗口级别录制的选择
  • 如果你在做游戏直播工具,搭配 “Support performance-intensive apps and games” 一起看,了解性能优化的全局策略
  • ReplayKit 的 iOS 实现已经有几年历史了,很多 macOS 上的 API 与 iOS 版本一致
WWDC 2020