引言
在前文章中,我们深入探讨了如何通过 AVCaptureSession 配置 iOS 中的捕捉输入及输出。并通过使用 AVCaptureDeviceInput 和 AVCapturePhotoOutput,我们实现了基础的照片捕获功能,并配置了 PHPreviewView 来显示实时预览。
在本篇中,我们将继续沿用前两篇的基本配置思路,但重点转向如何实现 高清视频和音频的录制。尽管大部分配置流程和前面的内容相似,主要区别在于,我们需要使用 AVCaptureMovieFileOutput 来处理视频录制,并结合音频输入和输出进行同步。通过这种方式,我们可以实现一个完整的多媒体录制系统,同时捕获视频和音频流。
搭建视频录制功能
为了更好的理解视频录制整体搭建流程,我们将其功能封装到一个 PHCaptureRecordController 类中,它将会负责视频录制的全部操作流程:
- 配置 AVCaptureSession。
- 添加摄像头输入。
- 添加视频录制输出。
- 启动与停止会话。
- 处理视频录制完成数据。
1. 类的基本结构
我们先来创建一个PHCaptureRecordController类,大致结构如下:
import UIKit
import AVFoundation
class 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 控制录制的启动与停止,并通过代理方法处理录制完成后的文件。
接下来,你可以根据项目需求进一步优化视频录制体验,例如添加实时预览、录制过程中动态调整质量,或者对录制的文件进行后期处理(如视频编辑、转码等)。
感谢阅读。