(七)深入了解AVFoundation-采集:采集系统架构与 AVCaptureSession 全面梳理

引言

在 iOS 开发中,AVFoundation 是构建音视频功能的强大底层框架。而在音视频功能中,"采集"往往是最基础也是最关键的一环。从摄像头捕捉图形、到麦克风获取声音,构建一条高效且稳定的采集链是开发高质量音视频应用的前提。

本系列将逐步深入 AVFoundation的采集机制,了解它的地方设计与实际使用。在本篇中,我们会聚集在采集系统的核心管理单元------AVCaptureSession,从整体架构出发,梳理采集链路的组成方式与搭建流程。

AVCaptureSession 的职责与系统架构

在 AVFoundation 中,AVCaptureSession 是整个采集系统的中心协调者。它的主要职责包括:

  1. 管理输入(Input)与输出(Output)设备:例如摄像头、麦克风作为输入,图像帧输出、音频数据输出作为输出。
  2. 协调数据流的流动:确保从设备捕获到的数据正确地传递到输出模块。
  3. 统一管理采集回话的生命周期:如开始采集startRunning()、停止采集stopRunning()、中断与恢复。
  4. 维护链接配置:控制比如视频方向、镜像、视频稳定等细节。

简而言之,AVCaptureSession 就像是一个数据总管,它把输入设备(比如摄像头、麦克风)和输出目标(比如屏幕预览、数据编码器)连接在一起,并统一管理整个采集链条的工作状态。

采集系统的基本架构可以理解成这样一个数据流动图。

简化来说就是:设备->输入->Session->输出->处理展示。

  • 一个 AVCaptureSession 可以同时管理多个输入输出(比如前后摄像头切换,或者同时采集视频与音频)。
  • 输入与输出设备必须通过 session.addInput() / session.addOutput() 正式添加到会话中,才会参与采集。
  • 建议在 专用串行 DispatchQueue 中配置 Session,避免 UI 卡顿。

输入 + 输出:构建采集模型的两大核心

在 AVFoundation 采集系统中,采集链条的两大基本构成单元就是:

  1. 输入(Input)
  2. 输出(Output)

理解输入输出的工作原理和使用方式,是搭建稳定采集系统的基础。

输入(Input):采集数据的源头

输入负责吧硬件设备(如摄像头、麦克风)链接到AVCaptureSession中。

在实际开发中,我们通常就是使用AVCaptureDeviceInput 来包装摄像头和麦克风。

以摄像头为例:

Swift 复制代码
// 1. 获取摄像头设备
guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
    fatalError("未找到后置摄像头")
}

// 2. 将设备包装成输入
let cameraInput = try AVCaptureDeviceInput(device: camera)

// 3. 添加到 Session
if session.canAddInput(cameraInput) {
    session.addInput(cameraInput)
}

注意事项:

  1. 添加输入前要用 canAddInput(_:) 检查是否可以添加。
  2. 注意捕获设备可能没有权限,需要提前请求授权。
  3. 创建 AVCaptureDeviceInput 可能抛异常,使用 try。

输出(Output):采集数据的去向

输出负责接收采集到的数据,并将其交给需要的地方处理,比如:

  • 显示预览画面
  • 实时处理图像
  • 保存到文件
  • 推流到服务器

输出的类型有很多种:

输出类别 相关类名 描述
图像数据输出 AVCaptureVideoDataOutput 原始视频帧数据回调
音频数据输出 AVCaptureAudioDataOutput 原始音频数据回调
拍照输出 AVCapturePhotoOutput 拍摄静态照片
录制文件输出 AVCaptureMovieFileOutput 录制成视频文件
元数据输出(如二维码) AVCaptureMetadataOutput 检测并返回元数据(条形码、二维码)

输出基本使用流程(以视频数据输出为例)

Swift 复制代码
// 1. 创建输出
let videoOutput = AVCaptureVideoDataOutput()

// 2. 配置输出参数(如像素格式)
videoOutput.videoSettings = [
    kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
]

// 3. 设置代理回调队列
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "VideoOutputQueue"))

// 4. 添加到 Session
if session.canAddOutput(videoOutput) {
    session.addOutput(videoOutput)
}

代理方法示例(捕获到每一帧数据):

Swift 复制代码
extension YourClass: AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // 处理每一帧图像
    }
}

注意事项:

  • 回调通常发生在自定义的串行队列上,避免占用主线程。
  • 输出设备也需要使用 canAddOutput(_:) 检查后再添加。

输入(Input) 负责采集硬件数据,输出(Output)负责把采集到的数据交给你处理或者保存。

输入输出就像水管的两端,中间由 AVCaptureSession 统一协调和流转。

搭建完整采集链条

在前面了解了 AVCaptureSession 的基本架构,以及输入(Input)和输出(Output)之后,这一部分,我们直接从实际角度出发,梳理一次完整搭建采集链条的流程。

完整流程

搭建完整采集链条,核心步骤可以总结为:

  1. 创建 AVCaptureSession
  2. 选择并创建输入设备(Input)
  3. 选择并创建输出设备(Output)
  4. 添加输入与输出到 Session
  5. 配置连接参数(如摄像头方向、镜像等)
  6. 创建预览层(可选)
  7. 启动 Session

整体的伪代码流程如下:

Swift 复制代码
// 1. 创建 Session
let session = AVCaptureSession()

// 2. 配置 preset
session.sessionPreset = .high

// 3. 创建输入(摄像头)
guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back),
      let cameraInput = try? AVCaptureDeviceInput(device: camera),
      session.canAddInput(cameraInput) else {
    return
}
session.addInput(cameraInput)

// 4. 创建输出(视频数据输出)
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "VideoOutputQueue"))
if session.canAddOutput(videoOutput) {
    session.addOutput(videoOutput)
}

// 5. 配置连接(方向、镜像)
if let connection = videoOutput.connection(with: .video) {
    connection.videoOrientation = .portrait
    connection.isVideoMirrored = false
}

// 6. 创建预览层(可选)
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)

// 7. 启动 Session
session.startRunning()

采集分辨率的控制开关

sessionPreset 负责定义采集的数据质量,比如分辨率、码率、帧率等。

常见的 preset 有:

Preset 说明
.high 高质量(设备自动适配)
.medium 中等质量,适配中速网络上传场景
.low 低质量,适合弱网传输
.hd1280x720 720p 高清
.hd1920x1080 1080p 高清
.photo 照片质量(高分辨率)

注意:

  • preset 需要在 startRunning() 之前设置。
  • preset 要与输入设备能力匹配,否则设置无效,比如某些前置摄像头不支持 4K 采集。
  • 可以使用 session.canSetSessionPreset(_:) 检查兼容性。

连接(AVCaptureConnection)管理

在 Session 里,输入与输出之间的通道,叫做 AVCaptureConnection。

连接对象允许我们进一步微调采集流的细节,比如:

  • 方向(Orientation)
  • 镜像(Mirrored)
  • 视频防抖(Video Stabilization)
  • 自动对焦/曝光/白平衡(某些连接可以关联控制)

一般情况下,我们需要手动设置采集方向为竖屏:

Swift 复制代码
if let connection = videoOutput.connection(with: .video),
   connection.isVideoOrientationSupported {
    connection.videoOrientation = .portrait
}

很多 app 前置摄像头拍照是镜像模式,也可以通过 Connection 设置:

Swift 复制代码
if camera.position == .front,
   let connection = videoOutput.connection(with: .video),
   connection.isVideoMirrored {
    connection.isVideoMirrored = true
}

线程注意点:采集是高度异步的

Session 的 startRunning 和 stopRunning 是同步调用,但异步完成,因此推荐在后台线程调用。

输出的数据回调 (captureOutput(_:didOutput:from:)) 是在你指定的 DispatchQueue里触发的,不是主线程。如果在回调里要更新 UI,需要回到主线程。

Swift 复制代码
DispatchQueue.global().async {
    session.startRunning()
}

// 回调中处理 UI
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    DispatchQueue.main.async {
        // 更新 UI,比如显示帧率
    }
}

结语

在本篇内容中,我们从整体视角出发,完整梳理了 AVCaptureSession 为核心的采集系统,包括它的职责、输入输出模型,以及如何搭建一条基本的采集链条。

同时也深入探讨了 preset 设置、连接管理 和 线程注意事项 等关键细节,帮助你在实际项目中少踩坑。

可以看到,AVFoundation 的采集体系虽然灵活强大,但也有一定的复杂度。理解 Session 是怎么组织 Input 和 Output 的,掌握 Connection 上的各种参数调优,才能真正驾驭底层采集系统。

接下来的博客我们将继续AVFoudation的采集篇章,我们将在这个基础上,进一步探索更多实战话题。

相关推荐
胖虎111 天前
(五)深入了解AVFoundation-播放:多音轨、字幕、倍速播放与横竖屏切换
avplayer·视频播放器·字幕·avfoundation·音轨
9527华安1 个月前
Xilinx系列FPGA视频采集转HDMI2.0输出,基于HDMI 1.4/2.0 Transmitter Subsystem方案,提供6套工程源码和技术支持
fpga开发·verilog·视频采集·hdmi2.0·4k
程序猿玖月柒3 个月前
常见的多媒体框架(FFmpeg GStreamer DirectShow AVFoundation OpenMax)
ffmpeg·音视频·gstreamer·openmax·directshow·avfoundation
亿牛云爬虫专家1 年前
实用技巧:在C和cURL中设置代理服务器爬取www.ifeng.com视频
c语言·开发语言·网络爬虫·爬虫代理·curl·视频采集·代理服务器
会头痛的可达鸭2 年前
三、视频设备的枚举以及插拔检测
音视频·视频采集