简介
一般拍照,直接使用UIImagePickerController
就可以了。使用方法简单,拍照效果很好。
当前有个应用,默认的拍照页面不能满足需求,比如要求拍多张才能退出拍照页面,所以就需要自定义。AVFoundation
框架下的AVCaptureSession
可以完成这个功能。
涉及的对象
// 捕获会话类
@property (strong, nonatomic) AVCaptureSession *session;
// 捕获设备类
@property (strong, nonatomic) AVCaptureDevice *device;
// 捕获设备输入类
@property (strong, nonatomic) AVCaptureDeviceInput *deviceInput;
// 捕获照片输出类
@property (strong, nonatomic) AVCapturePhotoOutput *photoOutput;
// 捕获连接类
@property (strong, nonatomic) AVCaptureConnection *connection;
// 捕获照片设置类
@property (strong, nonatomic) AVCapturePhotoSettings *photoSettings;
// 捕获视频预览层
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
1. AVCaptureSession
-
这个可以看做输入输出设备的整合者。
-
图片质量,或者视频质量在这里设置。
-
视频和图片使用这一套功能。
self.session = [[AVCaptureSession alloc] init];
// 预设为照片: AVCaptureSessionPresetPhoto 4032*3024
if ([self.session canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
}
2. AVCaptureDevice,AVCaptureDeviceInput
-
AVCaptureDevice
可以看做具体的设备,比如摄像头,麦克风等等。 -
AVCaptureDeviceInput
是配合AVCaptureSession
的输入设备,通过AVCaptureDevice
创建。 -
这两者可以视为一个整体,当做一个组件来看待。
-
所谓的“拍照”,相当于视频中的抓图片,所以这里选择视频设备
AVMediaTypeVideo
// 输入设备
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
self.deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.device error:&error];
if ([self.session canAddInput:self.deviceInput]) {
[self.session addInput:self.deviceInput];
}
3. AVCapturePhotoOutput
-
输出设备有很多种,有视频,有图片
-
图片的视频输出设备目前是
AVCapturePhotoOutput
-
AVCaptureConnection
和AVCapturePhotoSettings
可以看做是两个辅助对象,对照片进行各种设置。
要注意的是,竖屏还是横屏在
AVCaptureConnection
中设置。这里影响的是最后输出的照片的实际朝向。
// 输出设备
self.photoOutput = [[AVCapturePhotoOutput alloc] init];
if ([self.session canAddOutput:self.photoOutput]) {
[self.session addOutput:self.photoOutput];
}
self.connection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
// 固定横屏
if ([self.connection isVideoOrientationSupported]) {
self.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
self.photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey : AVVideoCodecTypeJPEG}];
4. AVCaptureVideoPreviewLayer
-
视频预览层。要注意的是这是layer,不是view;区别就是是否能够响应事件。单从显示来说,layer更高效一点。
-
大多数时候,这个layer都是给Controller的self.view,这样就导致全屏展示,这是没有必要的。只要加入要显示的view就可以了,很多时候没有必要全屏展示的。
-
视屏的显示模式分为三种。对应于图片的Aspect Fit; Aspect Fill; Scale toFill三种模式。最好能够和图片的content mode对应上,不然的话,会引起误解。
-
这个确实是“预览”,只是你看到的部分。比如说,这里也有对应
AVCaptureConnection
,也能这是竖屏还是横屏。但是这里设置的只是你能看到的,并不能设置照片实际的朝向。
// 照相视图
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
// 固定横屏
if(self.previewLayer.connection.isVideoOrientationSupported) {
self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
[self.photoView.layer insertSublayer:self.previewLayer atIndex:0];
启动,停止
-
这里的启动停止可以理解为扫描。一旦开始,就能在预览视图中看到视频界面。
-
启动过程比较耗时,最好不要在主线程做。
-
这个是通过
AVCaptureSession
这个对象来实现的。
// 开始扫描
- (void)start {
if (!self.session.isRunning) {
// 比较耗时,放入一个串行队列中单独执行
dispatch_queue_t queue = dispatch_queue_create("拍照的串行队列", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
[self.session startRunning];
});
}
}
// 停止扫描
- (void)stop {
if (self.session.isRunning) {
[self.session stopRunning];
}
}
拍照
-
拍照功能是通过设置代理的方式来完成的,并且还自带音效。这感觉非常奇怪。
-
并且默认不能重拍,需要用到一点小技巧才行。
// 照相按钮
- (IBAction)takePhotoButtonTouched:(id)sender {
// 防抖,0.5秒
self.photoButton.enabled = NO;
[NSTimer scheduledTimerWithTimeInterval:0.5 repeats:NO block:^(NSTimer * _Nonnull timer) {
self.photoButton.enabled = YES;
}];
// 拍照,设置代理就是拍照,确实有点奇怪
[self.photoOutput capturePhotoWithSettings:self.photoSettings delegate:self];
// 为下次拍照做准备,没有这句会崩溃;AVCapturePhotoSettings里面有一个uniqueID,不重新生成一下,会导致重复拍照崩溃
self.photoSettings = [AVCapturePhotoSettings photoSettingsFromPhotoSettings:self.photoSettings];
}
- 照片在代理方法中获得,不是很方便。这套API设计得非常差劲。
#pragma mark - AVCapturePhotoCaptureDelegate
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
if (error == nil) {
NSData *imageData = [photo fileDataRepresentation];
UIImage *image = [UIImage imageWithData:imageData];
// 这个image就是此次拍摄的照片
}
}
闪光灯
- 在AVCapturePhotoSettings中设定,这是最新的方法。以前在device中设定。
// 开闪光灯
- (void)switchOnFlash {
if ([self.photoOutput.supportedFlashModes containsObject:@(AVCaptureFlashModeOn)]) {
self.photoSettings.flashMode = AVCaptureFlashModeOn;
}
}
// 关闪光灯
- (void)switchOffFlash {
if ([self.photoOutput.supportedFlashModes containsObject:@(AVCaptureFlashModeOff)]) {
self.photoSettings.flashMode = AVCaptureFlashModeOff;
}
}
视频缩放
这个是AVCaptureDevice
提供的功能
// 视频缩放
- (void)zoomVideoWithFactor:(CGFloat)factor {
NSError *error = nil;
if ([self.device lockForConfiguration:&error]) {
if (factor < self.device.activeFormat.videoMaxZoomFactor) {
[self.device rampToVideoZoomFactor:factor withRate:10];
}
[self.device unlockForConfiguration];
} else {
[PDAHUD toast:error.localizedDescription];
}
}
网友评论