引言
工作中很多时候,系统的相机无法满足需求,这个时候需要就要自定义相机。
1.先把UI搭建好
拍照界面主要是相机的预览层以及拍完后照片的显示。这里是在初始化操作之后做的,但因为比较简单,我把它放在第一个步骤来写。
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer.frame = CGRectMake(0,kNavHeight,kScreenW, kScreenH-kBottomH-kNavHeight);
[self.view.layer insertSublayer:self.previewLayer atIndex:0];
AVCaptureVideoPreviewLayer是相机镜头捕捉到的预览层。videoGravity也是一个枚举,有点类似于UIImageView的contentMode。
2.自定义相机
自定义相机实现拍照、前后摄像头的切换、闪光灯、聚焦、放大缩小、拍照后预览、重拍等功能.
2.1 初始化操作
AVCaptureDevice
AVCaptureSession
AVCaptureDeviceInput
AVCaptureStillImageOutput
AVCaptureVideoPreviewLayer
AVCaptureConnection
UIDeviceOrientation
AVCaptureDevice
用来获取相机设备的一些属性,实现摄像头和麦克风的初始化操作. 闪光灯、手电筒、聚焦、曝光、白平衡等一些基本设置.这里我没有做对权限的控制,在实际的开发中是要做的.当用户点击不允许使用相机的时候,如果不做处理的话,就会显示黑屏甚至闪退.做权限处理,引导用户去设置页面打开摄像头权限.
参考文章
AVCaptureSession
AVCaptureSession是AVFoundation的核心类.用于捕捉音频和视频,协调视频和音频的输入和输出流之间的数据交换.我觉得这张图上的各个层级结构一目了然,可以帮助理解.
- (void)initCameraSettings
{
NSError *error;
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.session = [[AVCaptureSession alloc] init];
if ([self.session canSetSessionPreset:AVCaptureSessionPresetiFrame960x540]) {
self.session.sessionPreset = AVCaptureSessionPresetiFrame960x540;
}
self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
[self.imageOutput setOutputSettings:outputSettings];
...
}
2.1.1 设置SessionPreset
设置输出的画质。要判断是iPad还是iphone。sessionPreset是一个枚举,您可以点击进去看一下,结合实际项目中的需要进行选择。另外,值得注意的是:如果您设置的画质高于手机本身支持的分辨率的话那么会造成崩溃。比如,我现在设置的是 AVCaptureSessionPresetiFrame960x540大概需要摄像头支持50W的像素,而iphone4S的前置摄像头只有30W的像素,显然达不到标准。
2.1.2 设置Session的input
设置Session的输入源。可以是Video,也可以是Audio,或者两者都添加。
2.1.2 设置Session的output
设置Session的输出源。可以是图片源、音视频源、文件源等.这里用相机拍照,当然输出的是AVCaptureStillImageOutput图片源.AVCaptureStillImageOutput在iOS10中已经被AVCapturePhotoOutput所代替.
2.2 实现拍照功能
- (void)clickPhoto
{
self.connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
[self.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
[self.imageOutput captureStillImageAsynchronouslyFromConnection:self.connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:jpegData];
self.showImageView.image = image;
}];
}
这里的AVCaptureConnection是session和AVCaptureStillImageOutput连接的枢纽。通过回调拿到图片的数据,显示出来。这样就完成了基本的拍照功能。但如果把拍好的照片传给后台,会发现照片旋转了90度,明明在手机上是竖屏的,后台下载后却是横屏的。这是由于iphone的方向传感器造成的,遇到这种问题,只需要调整一下UIImage的imageOrientation属性即可。这个网上有现成的demo。如果您有兴趣,这篇文章我觉得写的非常细致。如何处理iOS中照片的方向,您可以参考。在这个基础上,再实现前置摄像头拍照。旋转摄像头的时候,可以再加一个翻转动画,当然这根据需求而定。
- (void)rotateCamera
{
NSArray *inputs = self.session.inputs;
for (AVCaptureDeviceInput *input in inputs ) {
AVCaptureDevice *device = input.device;
if ([device hasMediaType:AVMediaTypeVideo] ) {
AVCaptureDevicePosition position = device.position;
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
if(position == AVCaptureDevicePositionFront)
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
else
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
[self.session beginConfiguration];
[self.session removeInput:input];
[self.session addInput:newInput];
[self.session commitConfiguration];
break;
}
}
}
2.3 辅助功能
2.3.1 对焦、闪光灯
在预览层上加一层遮照View来响应手势的触发。聚焦功能如下。
if ([self.device lockForConfiguration:&error]) {
if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.device setFocusPointOfInterest:focusPoint];
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
}
[self.device unlockForConfiguration];
}
为了更好的用户体验,模仿系统相机的功能,在此基础上,增加一个动画效果。可以看到,系统相机有一个正方形的框,当点击的时候有一个缩放的效果。这里增加一个focusView,通过transform动画实现这个效果。
闪光灯的效果也比较简单,闪光灯有三种状态。这里只处理了闪光灯的开和关两种状态。
AVCaptureFlashModeOff = 0,
AVCaptureFlashModeOn = 1,
AVCaptureFlashModeAuto = 2,
[self.session beginConfiguration];
[self.device lockForConfiguration:nil];
[self.device setFlashMode:mode];
[self.device unlockForConfiguration];
[self.session commitConfiguration];
[self.session startRunning];
3.总结
以上,基本的相机功能就实现了。在这个基础上还可以增加很多功能。比如,把图片写进相册,从相册选取图片,以及图片的裁剪和小视频的录制等等就不在此篇赘叙。最近在做总结,关于之前写过的音视频采集、二维码扫描、图片合成视频等利用AVFoundation框架的功能会一一整理出来。另外,下面这篇博客是用swift实现的,功能很全面。我的代码还在整理中,之后也会同步到github。
Swift版本相机参考