首页 > 技术文章 > 关于自定义相机的整理

Lanht 2019-03-19 15:50 原文

  前言 

   为什么不用系统相机要用自定义相机呢?

  由于系统相机功能单一,界面简单,在开发的过程中为了达到更好的个性化交互设计,符合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,焦点位置

  点击屏幕时的焦点,要对应转化成摄像头上的坐标位置

  

推荐阅读