前言
为什么不用系统相机要用自定义相机呢?
由于系统相机功能单一,界面简单,在开发的过程中为了达到更好的个性化交互设计,符合app主题,提供更多更有趣的功能时候,我们会采用自定义相机。来达到更好的界面效果,更好的用户体验。接下来说说整个自定义相机过程,以及遇到的一些问题。
概览
1,流程
2,详细各部分
- 初始化,建立会话,获取摄像头
- 嵌入实时预览图层
- 相关设置
- 拍摄获取照片
- UIView层及UI交互
3,遇到的坑
流程
AVCaptureSession
通过把设备的麦克风/摄像头(AVCaptureDevice
)实例化成数据流输入对象(AVCaptureDeviceInput
)后,再通过建立连接(AVCaptionConnection
)将录制数据通过数据流输出对象(AVCaptureOutput
)导出,而录制的时候咱们可以同步预览当前的录制界面(AVCaptureVideoPreviewLayer
).
输出图像的时候需要用到AVCaptureConnection这个类,session通过AVCaptureConnection连接AVCaptureStillImageOutput进行图片输出,输入输出与connection的关系如下图
详细各部分
1.初始化,建立会话,获取摄像头
- (AVCaptureSession *)recordSession { if (_recordSession == nil) { _recordSession = [[AVCaptureSession alloc] init]; if ([_recordSession canAddInput:self.backCameraInput]) { [_recordSession addInput:self.backCameraInput]; } //将输入设备加入会话 if ([_recordSession canAddOutput:self.imageOutput]) { [_recordSession addOutput:self.imageOutput]; } //设置分辨率 if ([_recordSession canSetSessionPreset:AVCaptureSessionPresetPhoto]) { [_recordSession setSessionPreset:AVCaptureSessionPresetPhoto]; } } } //后置摄像头输入 - (AVCaptureDeviceInput *)backCameraInput { if (_backCameraInput == nil) { NSError *error; _backCameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backCamera] error:&error]; if (error) { NSLog(@"获取后置摄像头失败~"); } } return _backCameraInput; } //建立输出设备 - (AVCaptureStillImageOutput *)imageOutput { if (_imageOutput == nil) { _imageOutput = [[AVCaptureStillImageOutput alloc]init]; NSDictionary *settingDic = @{AVVideoCodecKey:AVVideoCodecJPEG}; _imageOutput.outputSettings = settingDic; } return _imageOutput; }
2.嵌入实时预览层
//捕获到的视频呈现的layer - (AVCaptureVideoPreviewLayer *)previewLayer { if (_previewLayer == nil) { //通过AVCaptureSession初始化 AVCaptureVideoPreviewLayer *preview = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.recordSession]; //AVLayerVideoGravityResizeAspect //设置比例为铺满全屏AVLayerVideoGravityResizeAspectFill //设置比例为和预览layer一样大小 AVLayerVideoGravityResize preview.videoGravity = AVLayerVideoGravityResizeAspectFill; _previewLayer = preview; } return _previewLayer; }
3.相关设置
//切换前后置摄像头 - (void)changeCameraInputDeviceisFront:(BOOL)isFront { if (isFront) { [self.recordSession stopRunning]; [self.recordSession removeInput:self.backCameraInput]; if ([self.recordSession canAddInput:self.frontCameraInput]) { [self changeCameraAnimation]; [self.recordSession addInput:self.frontCameraInput]; } } else { [self.recordSession stopRunning]; [self.recordSession removeInput:self.frontCameraInput]; if ([self.recordSession canAddInput:self.backCameraInput]) { [self changeCameraAnimation]; [self.recordSession addInput:self.backCameraInput]; } } } //开启闪光灯 - (void)openFlashLight { AVCaptureDevice *backCamera = [self backCamera]; if (backCamera.flashMode == AVCaptureFlashModeOff) { [backCamera lockForConfiguration:nil]; // backCamera.torchMode = AVCaptureTorchModeOn; backCamera.flashMode = AVCaptureFlashModeOn; [backCamera unlockForConfiguration]; } } //关闭闪光灯 - (void)closeFlashLight { AVCaptureDevice *backCamera = [self backCamera]; if (backCamera.flashMode == AVCaptureFlashModeOn) { [backCamera lockForConfiguration:nil]; // backCamera.torchMode = AVCaptureTorchModeOff; backCamera.flashMode = AVCaptureFlashModeOff; [backCamera unlockForConfiguration]; } } //聚焦 - (void)focusWithPoint:(CGPoint)aPoint { CGPoint focusPoint = [self.previewLayer captureDevicePointOfInterestForPoint:aPoint]; AVCaptureDevice *device = [self.recordSession.inputs.lastObject device]; if (device.isFocusPointOfInterestSupported && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { if ([device lockForConfiguration:nil]) { device.focusPointOfInterest = focusPoint; device.focusMode = AVCaptureFocusModeContinuousAutoFocus; [device unlockForConfiguration]; } } }
4.拍摄获取照片
- (void)takePhoto { AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo]; if (connection.isVideoOrientationSupported) { connection.videoOrientation = [self currentVideoOrientation]; } if ([connection isVideoStabilizationSupported]) { connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeCinematic; } [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { UIImage *image = nil; NSData *data = nil; if (imageDataSampleBuffer != NULL) { data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; image = [UIImage imageWithData:data]; } }]; }
遇到的坑
1,设置特殊分辨率的,再切换到前置摄像头闪退
这个问题的原因在于前置摄像头不具备后置摄像头的高分辨率时,切换时就会闪退,所以在切换摄像头时要判断摄像头支持的分辨率。
2,对焦和曝光
我在设置对焦是 先设置了模式setFocusMode,后设置对焦位置,就会导致很奇怪的现象,对焦位置是你上次点击的位置。所以一定要先设置位置,再设置对焦模式。
3,焦点位置
点击屏幕时的焦点,要对应转化成摄像头上的坐标位置