前言
为什么不用系统相机要用自定义相机呢?
由于系统相机功能单一,界面简单,在开发的过程中为了达到更好的个性化交互设计,符合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,焦点位置
点击屏幕时的焦点,要对应转化成摄像头上的坐标位置