ScreenCaptureKit是Mac 应用程序下的高性能屏幕录制库,当捕获新的视频帧和音频样本时,它会将它们作为包含媒体数据及其相关元数据的CMSampleBuffer对象传递给你的应用程序。
本文将为大家汇总一下ScreenCaptureKit的基本用法:
文末有完整demo
//获取是否可以录制,返回结果
var canRecord: Bool {
get async {
do {
// If the app doesn't have Screen Recording permission, this call generates an exception.
try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true)
return true
} catch {
return false
}
}
}
//更新当前可用window,apps,dislays
public func refreshAvailableContent(_ scwindows: (([SCWindow])->())? = nil, _ displays: (([SCDisplay])->())? = nil) async {
do {
// Retrieve the available screen content to capture.
let availableContent = try await SCShareableContent.excludingDesktopWindows(false,
onScreenWindowsOnly: true)
availableDisplays = availableContent.displays
let windows = filterWindows(availableContent.windows)
if windows != availableWindows {
availableWindows = windows
}
availableApps = availableContent.applications
if selectedDisplay == nil {
selectedDisplay = availableDisplays.first
}
if selectedWindow == nil {
selectedWindow = availableWindows.first
}
if displays != nil { displays!(availableDisplays) }
if scwindows != nil { scwindows!(availableWindows) }
} catch {
print("Failed to get the shareable content: \(error.localizedDescription)")
}
}
//创建流配置
private var streamConfiguration: SCStreamConfiguration {
let streamConfig = SCStreamConfiguration()
// 是否捕捉音频
streamConfig.capturesAudio = isAudioCaptureEnabled
streamConfig.excludesCurrentProcessAudio = false
// display的宽高设置
if captureType == .Display, let display = selectedDisplay {
streamConfig.width = display.width
streamConfig.height = display.height
}
// window宽高设置
if captureType == .Window, let window = selectedWindow {
streamConfig.width = Int(window.frame.width) * 2
streamConfig.height = Int(window.frame.height) * 2
}
// 1秒60帧设置
streamConfig.minimumFrameInterval = CMTime(value: 1, timescale: 60)
// 设置内存占用
streamConfig.queueDepth = 5
return streamConfig
}
//内容过滤器
private var contentFilter: SCContentFilter {
let filter: SCContentFilter
switch captureType {
case .Display:
guard let display = selectedDisplay else { fatalError("No display selected.") }
var excludedApps = [SCRunningApplication]()
//是否捕捉当前app内容
if isAppExcluded {
excludedApps = availableApps.filter { app in
Bundle.main.bundleIdentifier == app.bundleIdentifier
}
}
filter = SCContentFilter(display: display,
excludingApplications: excludedApps,
exceptingWindows: [])
case .Window:
guard let window = selectedWindow else { fatalError("No window selected.") }
filter = SCContentFilter(desktopIndependentWindow: window)
}
return filter
}
//进行scwindow的过滤
private func filterWindows(_ windows: [SCWindow]) -> [SCWindow] {
windows
.sorted { $0.owningApplication?.applicationName ?? "" < $1.owningApplication?.applicationName ?? "" }
.filter { $0.owningApplication != nil && $0.owningApplication?.applicationName != "" }
.filter { $0.owningApplication?.bundleIdentifier != Bundle.main.bundleIdentifier }
}
//开始捕捉
public func startCapture() {
do {
let config = streamConfiguration
let filter = contentFilter
stream = SCStream(filter: filter, configuration: config, delegate: self)
// Add a stream output to capture screen content.
try stream?.addStreamOutput(streamOutput!, type: .screen, sampleHandlerQueue: videoCaptureBufferQueue)
try stream?.addStreamOutput(streamOutput!, type: .audio, sampleHandlerQueue: audioCaptureBufferQueue)
stream?.startCapture()
isRunning = true
} catch {
isRunning = false
}
}
//停止捕捉
func stopCapture() {
guard isRunning else { return }
stream?.stopCapture()
}
//MARK: SCStreamOutput通过回调来处理samplebuffer
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
switch type {
case .audio:
break
case .screen:
DispatchQueue.main.async {
self.mtView.render(sampleBuffer)
}
break
default:
break
}
}