【iOS】自定义相机(八)拍照进阶

Live Photo

在前面【iOS】自定义相机(六)拍照录像中,我们介绍了如何使用AVCaptureStillImageOutput进行简单的拍照操作。但是从 iOS 10 开始,Apple 就弃用这个类并提供AVCapturePhotoOutput用于进行更多的拍照操作,比如拍摄动态照片(Live Photo)。AVCapturePhotoOutput是一个功能强大的类,在新系统中也不断有新的功能接入,比如iOS11支持双摄和获取深度数据、iOS12支持人像模式。不过,在本篇文章中主要介绍的还是他在 iOS10 中最基本的两项功能拍摄静态照片和动态图片

该部分的代码主要在SCCamera中的SCPhotoManager中

不管是拍摄静态照片,还是拍摄动态照片,AVCapturePhotoOutput的使用都有一个固定的形式:

  1. 捕捉会话启动前创建并设置AVCapturePhotoOutput对象
  2. 创建并配置AVCapturePhotoSettings对象以选择特定拍摄的功能和设置
  3. 调用capturePhotoWithSettings:delegate:方法进行操作

capturePhotoWithSettings:delegate:方法需要的两个参数分别是拍照方式设置拍照过程代理。拍照的配置将在下面的AVCapturePhotoSettings中介绍,至于拍照过程的代理调用顺序就穿插在后面的两种拍摄方式的处理过程中。

注意:

  • 由于AVCapturePhotoOutput已经包含AVCaptureStillImageOutput的功能,因此它们不可同时使用。
  • 由于动态照片其实是一段小小的视频,因此AVCapturePhotoOutput的动态照片启用的时候,AVCaptureMovieFileOutput是不可用的。

AVCapturePhotoSettings

AVCapturePhotoSettings用于选择在AVCapturePhotoOutput中特定拍摄的功能和设置。

常用属性:

  • format:类似于AVCaptureStillImageOutputoutputSettings属性,用于使用键值对配置。
    • 例如配置视频编码@{AVVideoCodecKey: AVVideoCodecJPEG}
  • flashMode:闪光灯模式
  • autoStillImageStabilizationEnabled:自动静态图片稳定(防抖)
  • highResolutionPhotoEnabled: 指定输出摄像头支持的最高质量图像
  • livePhotoMovieFileURL:动态照片保存路径

PS: 苹果规定,一次拍照操作对应一个AVCapturePhotoSettings实例,因此在AVCapturePhotoSettings中有一个uniqueID属性用于区分是否重用。所以,每次拍照之前,我们都要按照需要重新创建一个AVCapturePhotoSettings实例对象。

AVCapturePhotoCaptureDelegate

AVCapturePhotoCaptureDelegate基本

AVCaptureStillImageOutput中我们是直接在闭包中获取静态照片,并没有一个位置供我们告诉用户当前拍照过程。Apple 推出AVCapturePhotoCaptureDelegate为开发者提供提高用户体验的位置。使用AVCapturePhotoOutput拍照将会经过以下五个步骤

  1. 拍照配置的确定
    • willBeginCapture
  2. 开始曝光
    • willCapturePhoto
  3. 曝光结束
    • didCapturePhoto
  4. 拍照结果返回
    • didFinishProcessingPhoto
  5. 拍照完成
    • didFinishCapture

拍摄静态图片

主要操作是AVCapturePhotoSettings的创建与设置,预先设定好图片编码方式

- (void)takeStillPhoto:(AVCaptureVideoPreviewLayer*)previewLayer {
    // 设置照片方向
    AVCaptureConnection *connection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
    if (connection.supportsVideoOrientation) {
        connection.videoOrientation = previewLayer.connection.videoOrientation;
    }
    // 创建 AVCapturePhotoSettings
    AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];
    if ([self.photoOutput.availablePhotoCodecTypes containsObject:AVVideoCodecJPEG]) {
        NSDictionary *format = @{AVVideoCodecKey: AVVideoCodecJPEG};
        photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:format];
    }
    // 防抖
    photoSettings.autoStillImageStabilizationEnabled = YES;
    // 拍照
    [self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
}

静态图片获取

AVCapturePhotoCaptureDelegate中的didFinishProcessingPhotoSampleBuffer:中获取,具体裁剪方式和之前相似:

- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(nullable CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(nullable CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(nullable AVCaptureBracketedStillImageSettings *)bracketSettings error:(nullable NSError *)error) {
    // 1. 获取 originImage
    NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:photoSampleBuffer];
    UIImage *originImage = [[UIImage alloc] initWithData:imageData];
    originImage = [originImage fixOrientation];
    // 2. 获取 scaledImage
    CGFloat width = self.currentPreviewFrame.size.width;
    CGFloat height = self.currentPreviewFrame.size.height;
    CGFloat scale = [[UIScreen mainScreen] scale];
    CGSize size = CGSizeMake(width*scale, height*scale);
    UIImage *scaledImage = [originImage resizedImageWithContentMode:UIViewContentModeScaleAspectFill size:size interpolationQuality:kCGInterpolationHigh];
    // 3. 获取 croppedImage
    CGRect cropFrame = CGRectMake((scaledImage.size.width - size.width) * 0.5, (scaledImage.size.height - size.height) * 0.5, size.width, size.height);
    UIImage *croppedImage = [scaledImage croppedImage:cropFrame];
    // 4. 后续处理
    // ...
}

拍摄动态图片

硬件要求:iPhone 6s及以上机型
像素要求:不支持 AVCaptureSessionPresetHigh

在会话启动之前必须设置AVCapturePhotoOutput的属性livePhotoCaptureEnabledYES。然后在拍摄之前,AVCapturePhotoSettings的重点配置属性是livePhotoMovieFileURL

- (void)takeLivePhoto:(AVCaptureVideoPreviewLayer*)previewLayer {
    self.currentPreviewFrame = previewLayer.frame;
    self.livePhotoCompletion = completion;
    // 照片方向
    AVCaptureConnection *connection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
    if (connection.supportsVideoOrientation) {
        connection.videoOrientation = previewLayer.connection.videoOrientation;
    }
    // 创建 AVCapturePhotoSettings
    AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];
    // 设置动态图片保存路径
    NSString *livePhotoMovieFileName = [[NSUUID UUID] UUIDString];
    NSString *livePhotoMovieFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[livePhotoMovieFileName stringByAppendingPathExtension:@"mov"]];
    photoSettings.livePhotoMovieFileURL = [NSURL fileURLWithPath:livePhotoMovieFilePath];
    // 拍照
    [self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
}

AVCapturePhotoCaptureDelegate 调用过程

  1. willBeginCapture:设置完毕
  2. willCapturePhoto:开始曝光
  3. didCapturePhoto:结束曝光
  4. didFinishProcessingPhoto:静态照片获取位置
  5. didFinishRecordingLivePhotoMovie:结束动态照片拍摄
  6. didFinishProcessingLivePhotoToMovieFile动态照片结果处理(获取影片文件进行处理)
  7. didFinishCaptureForResolvedSettings:完成拍摄全过程

动态照片获取

Live Photo 是由一个图片和一个小视频组成的,即两个文件(.jpg+.mov)。因此我们需要在AVCapturePhotoCaptureDelegate中的didFinishProcessingPhoto获取静态照片,在didFinishProcessingLivePhotoToMovieFile中获取小视频。最后,我们将他们保存到相册的时候需要一起写入,系统会帮我们合成动态图片的。

官方文档

AVCapturePhotoOutput
Tracking Photo Capture Progress

你可能感兴趣的:(【iOS】自定义相机(八)拍照进阶)