(十)深入了解AVFoundation-采集:录制视频功能的实现
引言
在前文章中,我们深入探讨了如何通过 AVCaptureSession 配置 iOS 中的捕捉输入及输出。并通过使用 AVCaptureDeviceInput 和 AVCapturePhotoOutput,我们实现了基础的照片捕获功能,并配置了 PHPreviewView 来显示实时预览。
在本篇中,我们将继续沿用前两篇的基本配置思路,但重点转向如何实现 高清视频和音频的录制。尽管大部分配置流程和前面的内容相似,主要区别在于,我们需要使用 AVCaptureMovieFileOutput 来处理视频录制,并结合音频输入和输出进行同步。通过这种方式,我们可以实现一个完整的多媒体录制系统,同时捕获视频和音频流。
搭建视频录制功能
为了更好的理解视频录制整体搭建流程,我们将其功能封装到一个 PHCaptureRecordController 类中,它将会负责视频录制的全部操作流程:
- 配置 AVCaptureSession。
- 添加摄像头输入。
- 添加视频录制输出。
- 启动与停止会话。
- 处理视频录制完成数据。
1. 类的基本结构
我们先来创建一个PHCaptureRecordController类,大致结构如下:
import UIKit
import AVFoundationclass PHCaptureRecordController: NSObject {/// 会话let session = AVCaptureSession()/// 输出private let moviceFileOutput = AVCaptureMovieFileOutput()/// 输入private var captureDeviceInput: AVCaptureDeviceInput?/// 队列private let sessionQueue = DispatchQueue(label: "com.example.captureSession")/// 代理weak var delegate: PHCaptureProtocol?/// 配置会话func setupConfigureSession() {}/// 启动会话func startSession() {}/// 停止会话func stopSession() {}/// 开始录制func startRecording() {}/// 停止录制func stopRecording() {}}
可以看到我们在类的内部维护了:
- session:采集会话。
- moviceFileOutput:用于视频录制的会话输出。
- captureDeviceInput:当前使用的会话输入。
- sessionQueue:串行队列,保证采集相关操作的线程安全。
- delegate:代理,负责错误和结果的回调。
2. 配置采集会话
我们在 setupConfigureSession 方法中需要完成三个操作:
- 设置会话预设。
- 添加摄像头输入。
- 添加照片输出。
并且保证这些操作需要在会话 beginConfiguration 与 commitConfiguration 方法之间执行。
/// 配置会话func setupConfigureSession() {session.beginConfiguration()// 1.设置会话预设setupSessionPreset()// 2.设置会话输入if !setupSessionInput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Failed to add input"]))return}// 3.设置会话输出if !setupSessionOutput() {delegate?.captureError(NSError(domain: "PHCaptureController", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Failed to add output"]))return}session.commitConfiguration()}
2.1 设置会话预设
在配置 AVCaptureSession 时,我们需要选择适当的预设(preset)来确定录制视频的质量。AVCaptureSession 提供了多种预设选项,每个选项对应不同的视频分辨率和帧率。
例如,以下代码设置会话的预设为 .high,这意味着录制的视频将以较高的质量进行处理,适用于需要高清视频的场景:
/// 设置会话话预设private func setupSessionPreset() {session.sessionPreset = .high}
2.2 设置会话输入
在视频录制中我们要同时设置摄像头及麦克风的输入,并确保这两个输入都成功被添加到会话,有一个失败则认为是设置失败。
/// 设置会话输入private func setupSessionInput(device: AVCaptureDevice? = nil) -> Bool {// 1.获取摄像头guard let device = device else { return false }do {captureDeviceInput = try AVCaptureDeviceInput(device: device)if session.canAddInput(captureDeviceInput!) {session.addInput(captureDeviceInput!)} else {return false}} catch {delegate?.captureError(error)return false}//2.获取麦克风guard let audioDevice = AVCaptureDevice.default(for: .audio) else { return false }do {let audioInput = try AVCaptureDeviceInput(device: audioDevice)if session.canAddInput(audioInput) {session.addInput(audioInput)return true} else {return false}} catch {delegate?.captureError(error)return false}}
2.3 设置回话输出
在完成输入设备的配置之后,我们需要设置输出设备,以便从会话中捕获视频、音频数据。
/// 设置会话输出private func setupSessionOutput() -> Bool {if session.canAddOutput(moviceFileOutput) {session.addOutput(moviceFileOutput)return true} else {return false}}
3. 会话的启动与停止
我们同样使用单独的串行队列来管理会话的启动与停止,避免出现线程问题。
/// 启动会话func startSession() {sessionQueue.async {if !self.session.isRunning {self.session.startRunning()}}}/// 停止会话func stopSession() {sessionQueue.async {if self.session.isRunning {self.session.stopRunning()}}}
4. 开始与停止录制
在配置好输入设备和输出设备后,我们可以通过 AVCaptureMovieFileOutput 来控制视频的录制。AVCaptureMovieFileOutput 提供了两个主要方法:startRecording 和 stopRecording,用于开始和停止视频录制。
开始录制
开始录制时,我们需要指定一个文件保存路径,并调用 startRecording 方法开始录制。以下是实现代码:
/// 开始录制
func startRecording() {// 设置输出文件路径let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("output.mov")// 启动录制moviceFileOutput.startRecording(to: outputURL, recordingDelegate: self)
}
- 输出路径:我们使用 FileManager.default.temporaryDirectory 来创建一个临时文件夹,并为录制的视频指定一个保存路径。这里保存的是 .mov 格式的视频文件。
- 启动录制:调用 startRecording(to: recordingDelegate:) 方法,传入输出文件路径和录制的代理对象。代理对象负责处理录制过程中产生的各种事件,例如录制的开始、停止、失败等。
停止录制
录制完成后,我们需要调用 stopRecording 方法来结束录制,并将录制的视频保存到指定的文件路径中。以下是停止录制的代码实现:
/// 停止录制
func stopRecording() {// 停止录制moviceFileOutput.stopRecording()
}
调用 stopRecording() 后,AVCaptureMovieFileOutput 会停止录制视频,并将录制的视频保存到先前指定的输出路径。此时,代理方法会被触发,我们可以在代理方法中处理保存的文件或进行其他后续操作。
5. 录制完成的代理方法
当视频录制完成时,AVCaptureMovieFileOutput 会调用 AVCaptureFileOutputRecordingDelegate 的 fileOutput(_:didFinishRecordingTo:from:connections:error:) 方法。我们可以通过这个方法获取录制的文件路径,并处理录制结果。
以下是代理方法的代码实现:
// MARK: AVCaptureFileOutputRecordingDelegate
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: (any Error)?) {if let error = error {// 录制过程中发生错误,调用错误回调delegate?.captureError(error)} else {// 录制完成,处理视频文件// 可以将 outputFileURL 移动到你想要保存的位置delegate?.captureVideo(outputFileURL)}
}
- outputFileURL:这是录制视频的输出文件 URL,包含了录制完成的视频文件路径。你可以根据需要将其移动或拷贝到其他位置进行后续处理。
- connections:表示输出连接的数组,通常情况下,我们只关心视频录制的连接。
- error:如果录制过程中出现错误,该参数会包含错误信息。我们可以检查并处理错误。
结语
在本篇中,我们详细介绍了如何在 iOS 中实现高清视频与音频的录制。从配置 AVCaptureSession 和添加输入输出设备,到设置视频和音频的录制输出,我们逐步完成了录制会话的初始化和控制。特别是在开始与停止录制部分,我们展示了如何通过 AVCaptureMovieFileOutput 控制录制的启动与停止,并通过代理方法处理录制完成后的文件。
接下来,你可以根据项目需求进一步优化视频录制体验,例如添加实时预览、录制过程中动态调整质量,或者对录制的文件进行后期处理(如视频编辑、转码等)。
感谢阅读。