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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容