iOS自定义相机

引言

工作中很多时候,系统的相机无法满足需求,这个时候需要就要自定义相机。

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的核心类.用于捕捉音频和视频,协调视频和音频的输入和输出流之间的数据交换.我觉得这张图上的各个层级结构一目了然,可以帮助理解.

image.png

- (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版本相机参考

推荐阅读更多精彩内容