AVCaptureSession
是媒体捕捉中核心类,因此会话的创建与配置是做好自定义相机的第一步。这篇文章将会介绍下图会话配置的过程以及注意事项。
具体的会话配置代码可以参考SCCamera中的SCCameraController.m代码
会话创建
- 会话的创建十分简单,只要单纯的调用
[[AVCaptureSession alloc] init]
即可 - 设置
sessionPreset
,即设置会话输出的分辨率
配置输入
- 获取硬件设备
- 创建捕捉输入
- 添加到会话中
- (void)setupSessionInput:(NSError**)error {
// 视频输入(默认是后置摄像头)
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoDevice error:error];
if ([_session canAddInput:videoInput]) {
[_session addInput:videoInput];
}
// 音频输入
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:error];
if ([_session canAddInput:audioInput]){
[_session addInput:audioInput];
}
}
PS: 每次添加输入或者输出,都需要调用addInput
判断是否可添加该输入或输出
配置预览视图
重要的操作是设置AVCaptureVideoPreviewLayer
的session
属性,将预览视图与使用的捕捉会话绑定在一起。剩下的预览显示工作就可以交给 AVFoundation 去完成了。
dispatch_async(dispatch_get_main_queue(), ^{
self.cameraView.previewView.captureSession = self.session;
});
PS:很多时候我们会话配置都会选择在子线程中执行,然而CALayer
的操作(即UI操作)必须放在主线程中。因此,我们需要回到主线程去执行预览视图的配置。
配置输出
- (void)setupSessionOutput:(NSError**)error {
// 照片输出
AVCapturePhotoOutput *photoOutput = [AVCapturePhotoOutput new];
if ([_session canAddOutput:photoOutput]) {
[_session addOutput:photoOutput];
}
// 视频文件输出
AVCaptureMovieFileOutput *movieFileOutput = [AVCaptureMovieFileOutput new];
if ([_session canAddOutput:movieFileOutput]) {
[_session addOutput:movieFileOutput];
}
}
- 配置输出和输入的步骤十分相似,并且都需要判断会话是否可添加。这一步虽然看上去比较多余,但是还是需要有这个判断的习惯
-
AVCapturePhotoOutput
是iOS 10新增的用于拍照的输出类。他有和AVCaptureStillImageOutput
一样的静态图片获取能力,也有拍摄 Live 照片的能力。(AVCaptureStillImageOutput
已被标记为弃用) - ※ 如果使用了
AVCaptureMovieFileOutput
,那么AVCaptureVideoDataOutput
和AVCaptureAudioDataOutput
将不会工作
配置整合
在对会话进行配置的时候,需要通过beginConfiguration
和commitConfiguration
方法进行单独的、原子性的操作。
- (void)configureSession:(NSError**)error {
[self.session beginConfiguration];
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
[self setupSessionInput:error];
dispatch_async(dispatch_get_main_queue(), ^{
// 在添加视频输入后就可以设置
self.cameraView.previewView.captureSession = self.session;
});
[self setupSessionOutput:error];
[self.session commitConfiguration];
}
在进行会话配置的时候,一般我们还需要做权限的判断,在有所有必须的权限后才进行会话的配置。
if (!self.hasAllPermissions) {
// TODO: - 没有权限的操作
} else {
dispatch_async(self.sessionQueue, ^{
NSError *error;
[self configureSession:&error];
// TODO: - 处理配置会话错误情况
});
}
由于会话配置的过程不是UI操作,也算是一个耗时操作,因此我们可以将会话配置的操作放在子线程中执行。常用的方式就是创建一个同步队列(self.sessionQueue
),并进行异步执行,达到将会话操作都放在子线程中顺序同步执行的效果。
会话运行与停止
在会话配置后我们需要调用startRunning
和stopRunning
进行会话的运行与停止。当然,这个操作也需要放在self.sessionQueue
中,我们也需要保证startRunning
在会话配置操作后执行。
// 会话运行
dispatch_async(self.sessionQueue, ^{
if (self.hasAllPermissions && !self.session.isRunning) {
[self.session startRunning];
}
});
// 会话停止
dispatch_async(self.sessionQueue, ^{
if (self.session.isRunning) {
[self.session stopRunning];
}
});
会话的运行与停止,就代表了AVFoundation的工作与否。在不需要用到当前AVFoundation工作的时候,停止会话可以节省系统开支。
网友评论