1. 概述
AVCaptureSession 是 AVFoundation 的核心类,用于管理捕获对象AVCaptureInput 的视频和音频的输入,协调捕获的输出AVCaptureOutput。
AVCaptureOutput 的输出有两种方法:
- 直接以 movieFileUrl 方式输出,基于 AVCaptureSession + AVCaptureMovieFileOutput;
- 以原始数据流 data 的方式输出,基于AVCaptureSession + AVAssetWriter。
两种流程对比,如下:
两种流程对比2. 对于 AVCaptureSession + AVCaptureMovieFileOutput
1)创建AVCaptureSession
//导入 AVFoundation.framework
#import <AVFoundation/AVFoundation.h>
//声明属性
@property (nonatomic, strong) AVCaptureSession *captureSession;
//懒加载 AVCapturesession
- (AVCaptureSession *)captureSession {
if (!_captureSession) {
_captureSession = [[AVCaptureSession alloc] init];
//设置分辨率
if ([_captureSession canSetSessionPreset:AVCaptureSessionPresetHigh]) {
[_captureSession setSessionPreset:AVCaptureSessionPresetHigh];
}
}
return _captureSession;
}
// 注意:AVCaptureSession 的调用是会阻塞线程的,建议单独开辟子线程处理
2)设置音频、视频输入
//声明属性
@property (nonatomic, strong) AVCaptureDeviceInput *videoInput;
@property (nonatomic, strong) AVCaptureDeviceInput *audioInput;
//设置视频,音频输入源
- (void)setCaptureDeviceInput {
//1\. 视频输入源
//获取视频输入设备, 默认后置摄像头
AVCaptureDevice *videoCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
self.videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCaptureDevice error:&error];
if ([self.captureSession canAddInput:self.videoInput]) {
[self.captureSession addInput:self.videoInput];
}
//2\. 音频输入源
AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
self.audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];
if ([self.captureSession canAddInput:self.audioInput]) {
[self.captureSession addInput:self.audioInput];
}
}
3)设置文件输出源
//声明属性
@property (nonatomic, strong) AVCaptureMovieFileOutput *movieFileOutput;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
//设置文件输出源
- (void)setDeviceFileOutput {
//初始化文件输出对象
self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
//捕获会话中特定捕获输入对象和捕获输出对象之间的连接
AVCaptureConnection *captureConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
//设置防抖
if ([captureConnection isVideoStabilizationSupported]) {
captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
//预览图层和视频方向保持一致
captureConnection.videoOrientation = [self.previewLayer connection].videoOrientation;
//添加文件输出源
if ([self.captureSession canAddOutput:self.movieFileOutput]) {
[self.captureSession addOutput:self.movieFileOutput];
}
}
4)添加视频预览层
- (void)setVideoPreviewLayer {
self.previewLayer.frame = [UIScreen mainScreen].bounds;
[self.superView.layer addSubLayer:self.previewLayer];
}
- (AVCaptureVideoPreviewLayer *)previewLayer {
if (!_previewLayer) {
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
_previewLayer.masksToBounds = YES;
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;//填充模式
}
return _previewLayer;
}
5)开始采集
//声明属性
@property (nonatomic, strong) dispatch_queue_t sessionQueue;
//开始采集
- (void)startCapture {
self.sessionQueue = dispatch_queue_create("com.capturesession.queue", DISPATCH_QUEUE_CONCURRENT);
if (![self.captureSession isRunning]) {
__weak __typeof(self) weakSelf = self;
dispatch_async(self.sessionQueue, ^{
[weakSelf.captureSession startRunning];
});
}
}
6)开始录制
//开始录制
- (void)startRecord {
[self.movieFileOutput startRecordingToOutputFileURL:[self createVideoPath] recordingDelegate:self];
}
当实际的录制开始或停止时,系统会有代理回调。当开始录制之后,这时可能还没有真正写入,真正开始写入会回调下面代理,停止录制也是如此,所以如果你需要对录制视频起始点操作,建议通过系统的回调代理:
//实现协议 <AVCaptureFileOutputRecordingDelegate>中的方法
#pragma mark _ AVCaptureFileOutputRecordingDelegate
//起始点 - 开始录制
- (void)captureOutput:(AVCaptureFileOutput *)output didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray<AVCaptureConnection *> *)connections {
}
//结束录制
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(@"视频录制完成. 文件路径:%@",[outputFileURL absoluteString]);
}
7)停止录制
//停止录制
- (void)stopRecord {
if ([self.movieFileOutput isRecording]) {
[self.movieFileOutput stopRecording];
}
}
8)停止采集
//停止采集
- (void)stopCapture {
if ([self.captureSession isRunning]) {
__weak __typeof(self) weakSelf = self;
dispatch_async(self.sessionQueue, ^{
[weakSelf.captureSession stopRunning];
weakSelf.captureSession = nil;
});
}
}
3. 基于 AVCaptureSession + AVAssetWriter
在iOS开发中,我们经常需要使用摄像头来录制视频。AVCaptureSession是iOS中用于捕获音频和视频的核心类之一,而AVAssetWriter是一个简单易用的类,用于将音频和视频捕捉到的数据写入到一个文件中。本文将介绍如何使用AVCaptureSession和AVAssetWriter来录制视频,并提供相应的代码示例。
1)设置AVCaptureSession
首先,创建一个AVCaptureSession实例,并配置其输入和输出
// 初始化AVCaptureSession
let captureSession = AVCaptureSession()
// 设置摄像头设备
guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
guard let videoInput = try? AVCaptureDeviceInput(device: videoDevice) else { return }
if captureSession.canAddInput(videoInput) {
captureSession.addInput(videoInput)
}
// 设置音频设备
guard let audioDevice = AVCaptureDevice.default(for: .audio) else { return }
guard let audioInput = try? AVCaptureDeviceInput(device: audioDevice) else { return }
if captureSession.canAddInput(audioInput) {
captureSession.addInput(audioInput)
}
// 配置输出
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global())
if captureSession.canAddOutput(videoOutput) {
captureSession.addOutput(videoOutput)
}
let audioOutput = AVCaptureAudioDataOutput()
audioOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global())
if captureSession.canAddOutput(audioOutput) {
captureSession.addOutput(audioOutput)
}
在上述代码中,我们首先创建了一个AVCaptureSession实例,然后设置了视频和音频的输入设备,并将其添加到captureSession中。接下来,我们配置了视频和音频的输出,并将其添加到captureSession中。
2)创建AVAssetWriter
接下来,我们需要创建一个AVAssetWriter实例,并设置其输出文件的路径和格式。
// 设置输出文件路径
let outputPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/output.mp4"
let outputURL = URL(fileURLWithPath: outputPath)
// 创建AVAssetWriter实例
guard let assetWriter = try? AVAssetWriter(url: outputURL, fileType: .mp4) else { return }
在上述代码中,我们首先使用NSSearchPathForDirectoriesInDomains方法获取了输出文件的路径,并创建了相应的URL。然后,我们使用AVAssetWriter的init(url:fileType:)方法创建了一个AVAssetWriter实例。
3)配置AVAssetWriter输入
接下来,我们需要配置AVAssetWriter的输入,以便将捕获到的音视频数据写入到输出文件中。
// 配置视频输入
let videoSettings: [String: Any] = [
AVVideoCodecKey: AVVideoCodecType.h264,
AVVideoWidthKey: 1280,
AVVideoHeightKey: 720
]
let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
videoInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(videoInput) {
assetWriter.add(videoInput)
}
// 配置音频输入
let audioSettings: [String: Any] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey: 2,
AVSampleRateKey: 44100,
AVEncoderBitRateKey: 128000
]
let audioInput = AVAssetWriterInput(mediaType: .audio, outputSettings: audioSettings)
audioInput.expectsMediaDataInRealTime = true
if assetWriter.canAdd(audioInput) {
assetWriter.add(audioInput)
}
在上述代码中,我们首先配置了视频输入的输出设置,指定了视频编码格式、视频宽度和高度。然后,我们创建了一个AVAssetWriterInput实例,并将其添加到assetWriter中。接下来,我们配置了音频输入的输出设置,指定了音频格式、声道数、采样率和比特率,并将其添加到assetWriter中。
4)开始录制和停止录制
最后,我们需要通过AVCaptureSession的startRunning()方法开始录制,并通过AVAssetWriter的finishWriting(completionHandler:)方法停止录制。
// 开始录制
captureSession.startRunning()
// 开始写入
assetWriter.startWriting()
// 停止写入
assetWriter.finishWriting(completionHandler: {
if self.assetWriter.status.rawValue == 2 {
DispatchQueue.main.async {
guard let callback = self.finishWriteCallback else {
return
}
callback(self.assetWriter.outputURL)
}
}
})
4. 以上两种方式间的区别
主要在于输出的数据格式和处理方式,以及适用场景:
- 以movieFileUrl方式输出(AVCaptureMovieFileOutput):
- 输出的是已经编码好的视频文件,直接以文件的形式保存在设备上。
- 适用于需要简单录制视频并保存为文件的场景,比如拍摄视频、录制直播等。
- AVCaptureMovieFileOutput负责将捕获到的音视频数据直接编码并保存为文件,对于开发者来说操作简单,不需要处理原始数据流,因此适用于对音视频处理不深入的场景。
- 以原始数据流data的方式输出(AVAssetWriter):
- 输出的是未经编码的原始音视频数据流,需要开发者自行处理数据的编码、封装等操作。
- 适用于需要对音视频数据进行更加灵活的处理、编辑或传输的场景,比如实时处理、编辑、加水印、实时传输等。
- 使用AVAssetWriter需要开发者手动实现数据的编码和写入,相比起直接保存文件的方式更加灵活,但也更加复杂,需要开发者对音视频编码、数据封装等有一定的了解和技能。
综上所述,如果你只需要简单地录制视频并保存为文件,可以选择使用AVCaptureMovieFileOutput。而如果需要对音视频数据进行更复杂的处理,比如实时编辑、加工或传输,那么就需要使用AVAssetWriter以原始数据流方式输出,并自行处理数据。
参考链接:
https://www.cnblogs.com/reyzhang/p/16646673.html
https://blog.51cto.com/u_16175437/6672987
网友评论