Mac OS 使用ScreenCaptureKit进行窗口抓取和系统声音抓取

ScreenCaptureKit

概述

ScreenCaptureKitMac 应用程序下的高性能屏幕录制库,当捕获新的视频帧和音频样本时,它会将它们作为包含媒体数据及其相关元数据的CMSampleBuffer对象传递给你的应用程序。
本文将为大家汇总一下ScreenCaptureKit的基本用法:

  • 获取所有的screen内容
  • 获取所有的window内容
  • 过滤screen及window的方法
  • 获取系统声音
  • 过滤当前app的内容及声音
  • 使用Metal渲染捕捉流
  • 如何切换采集对象并渲染

文末有完整demo

内容

Shareable content

  • SCShareableContent:将返回displayswindowsapplications,需要配合SCContentFilterSCStream过滤使用。
  • SCDisplay:表示显示设备的对象。
  • SCRunningApplication:表示在设备上运行的应用程序的对象。
  • SCWindow:表示屏幕窗口的对象。

Content capture

  • SCStream:表示可共享内容流的对象。
  • SCStreamConfiguration:为流提供输出配置的对象。
  • SCContentFilter:过滤流捕获的内容的对象。
  • SCStreamDelegate:用于响应流事件的委托协议。

Output processing

  • SCStreamOutput:用于接收流输出事件的协议。
  • SCStreamOutputType:表示流的输出类型的常量。
  • SCStreamFrameInfo:定义用于从系统捕获的帧中检索元数据的键的结构。
  • SCFrameStatus:流中帧的状态值。

Stream errors

  • SCStreamErrorDomain:字符串表示形式的错误内容。
  • SCStreamError:表示框架错误的结构。

创建一个捕捉程序(如图示例)

实例gif图

  • 首先获取权限,判断是否获取到屏幕录制权限:
//获取是否可以录制,返回结果
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
    }
}

Demo链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
好的,以下是一个利用 Python 进行网页数据抓取和清洗的实例: 首先,需要安装 requests 和 BeautifulSoup 库,可以使用以下命令进行安装: ``` pip install requests pip install beautifulsoup4 ``` 然后,我们可以使用 requests 库来获取网页内容,然后使用 BeautifulSoup 库对网页进行解析和清洗。 假设我们想要获取某个新闻网站上的新闻标题和链接,可以使用以下代码: ```python import requests from bs4 import BeautifulSoup url = "https://www.example.com/news" # 发送 GET 请求并获取网页内容 response = requests.get(url) html = response.text # 使用 BeautifulSoup 解析网页内容 soup = BeautifulSoup(html, "html.parser") # 找到包含新闻标题和链接的 HTML 元素 news_list = soup.find_all("div", class_="news-item") # 解析 HTML 元素,获取新闻标题和链接 for news_item in news_list: title = news_item.find("a", class_="news-title").text link = news_item.find("a", class_="news-title")["href"] # 输出新闻标题和链接 print(title) print(link) ``` 在上面的代码中,我们首先发送了一个 GET 请求,获取了网页的 HTML 内容。然后,我们使用 BeautifulSoup 对 HTML 内容进行解析,找到包含新闻标题和链接的 HTML 元素,并解析获取标题和链接。最后,我们输出了每条新闻的标题和链接。 需要注意的是,有些网站可能会对爬虫进行限制或者禁止爬虫,所以在进行网页数据抓取时需要注意一些法律和道德问题,同时需要保护自己的 IP 地址,防止被封禁。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄权浩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值