捕获照片-AVCapturePhotoOutput

在iOS 10之前,自定义相机一般使用AVCaptureStillImageOutput实现。但是AVCaptureStillImageOutput在iOS 10以后已经被弃用了。所以我们使用AVCapturePhotoOutput来实现。AVCapturePhotoOutput的功能自然会更加强大,不仅支持简单的JPEG图片拍摄,还支持Live照片和RAW格式拍摄。AVCapturePhotoOutput是一个功能强大的类,在新系统中也不断有新的功能接入,比如iOS11支持双摄和获取深度数据,iOS 12支持人像模式等。

简单使用

1. 首先初始化,按照需要参数初始化就行了,和AVCaptureStillImageOutput差别不大.

    self.imageOutput = [[AVCapturePhotoOutput alloc] init];
    NSDictionary *setDic = @{AVVideoCodecKey:AVVideoCodecJPEG};
    _outputSettings = [AVCapturePhotoSettings photoSettingsWithFormat:setDic];
    [self.imageOutput setPhotoSettingsForSceneMonitoring:_outputSettings];
    [self.session addOutput:self.imageOutput];
    self.preview = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    [self.preview setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [self.preview setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    [self.layer addSublayer:self.preview];
    [self.session startRunning];

2. 实现照片获取

AVCaptureStillImageOutput中使用captureStillImageAsynchronouslyFromConnection在block中直接可以获取到图片,AVCapturePhotoOutput需要实现AVCapturePhotoCaptureDelegate协议,在协议中获取。
获取当前屏幕图片输出:

[self.imageOutput capturePhotoWithSettings:_outputSettings delegate:self];

实现AVCapturePhotoCaptureDelegate协议,并在didFinishProcessingPhotoSampleBuffer方法中获取图片:

- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingPhotoSampleBuffer:(nullable CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(nullable CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(nullable AVCaptureBracketedStillImageSettings *)bracketSettings error:(nullable NSError *)error {
    
    NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    UIImage *image = [UIImage imageWithData:data];
    
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

使用步骤

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

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

capturePhotoWithSettings:delegate:方法需要的两个参数分别是拍照方式设置和拍照过程代理。

注意:
1 由于AVCapturePhotoOutput已经包含AVCaptureStillImageOutput的功能,因此它们不能同时使用。
2 由于动态照片其实是一段小小的视频,因此AVCapturePhotoOutput不支持同时进行动态照片和电影文件输出。如果AVCaptureSession包含AVCaptureMovieFileOutput对象,则livePhotoCaptureSupported属性将变为NO。作为替代方案,可以使用AVCaptureVideoDataOutput类与动态照片拍摄相同的分辨率输出视频数据。

AVCapturePhotoSettings

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

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

苹果规定,一次拍照操作对呀一个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) {
    NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    UIImage *image = [UIImage imageWithData:data];
}

拍摄动态图片

是否支持 live Photo
//当前是否支持动态照片拍摄。
@property(nonatomic, readonly, getter=isLivePhotoCaptureSupported)
BOOL livePhotoCaptureSupported;

并非所有设备和捕获格式都支持动态照片拍摄。如果当前AVCaptureSession的sessionPreset属性或AVCaptureDevice的activeFormat属性发生更改,则此属性的值可能会更改。如果相机或格式更改导致此属性的值变为NO,则livePhotoCaptureEnabled属性的值也将变为NO。

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

是否拍摄 live Photo

是否拍摄动态照片;在AVCapturePhotoSettings的livePhotoMovieFileURL属性为非nil、启动照片拍摄之前,必须启用此选项。启用此选项后,可以随时发起动态照片和静态照片的拍摄请求。

@property(nonatomic, getter=isLivePhotoCaptureEnabled)
BOOL livePhotoCaptureEnabled;

拍摄动态照片要求AVCaptureSession以不同方式设置其内部渲染管道。如果打算完全拍摄动态照片,在调用AVCaptureSession的 -startRunning方法之前将此属性设置为YES。在AVCaptureSession运行时更改此属性需要对捕获渲染管道进行冗长的重新配置:正在进行的动态照片捕获将立即结束,未实现的照片请求将中止,视频预览将暂时冻结。

在会话启动之前必须设置AVCapturePhotoOutput的属性livePhotoCaptureEnabled为YES。然后在拍摄之前,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];
}
是否暂停 live Photo
//是否暂停但不禁用动态照片拍摄
@property(nonatomic, getter=isLivePhotoCaptureSuspended)
BOOL livePhotoCaptureSuspended;

使用此属性可以缩短当前正在进行的动态照片动画捕捉:例如,如果突然需要执行一些不希望在动态照片电影中显示的内容,例如拍摄快照的静态照片声音。

默认情况下,此属性的值为NO。当将值更改为YES时,正在进行的任何动态照片拍摄将被剪裁到当前时间。同样,当此属性的值从YES更改为NO时,后续的动态照片拍摄将不会包含任何比取消暂停的动态照片拍摄更早的示例。

如果livePhotoCaptureEnabled属性的值为NO,则将此属性设置为YES会引发异常NSInvalidArgumentException。

是否自动修剪 live Photo

是否自动修剪动态照片拍摄以避免过度移动;当livePhotoCaptureSupported为YES时,此属性的值也默认为YES。

@property(nonatomic, getter=isLivePhotoAutoTrimmingEnabled)
BOOL livePhotoAutoTrimmingEnabled;

使用此选项可启用“相机”应用中的相同自动修剪行为。默认情况下,动态照片拍摄持续时间约为三秒:以拍摄请求的时间为中心。 但是,如果用户在拍摄期间升高或降低相机,iOS会实时分析电影捕捉并自动修剪实时照片的持续时间,以避免捕获过多的移动。

启用或禁用此功能要求AVCaptureSession以不同方式设置其内部呈现管道。为获得最佳结果,在调用AVCaptureSession的 -startRunning方法之前更改此属性的值。会话运行时更改此属性需要对捕获渲染管道进行冗长的重新配置:正在进行的动态照片捕获将立即结束,未实现的照片请求将中止,视频预览将暂时冻结。

AVCapturePhotoCaptureDelegate 调用过程

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

动态照片获取

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

获取格式化输出数据

1. 获取JPEG格式的数据
+ (NSData *)JPEGPhotoDataRepresentationForJPEGSampleBuffer:(CMSampleBufferRef)JPEGSampleBuffer
previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer;

返回与指定样本缓冲区中捕获的照片对应的JPEG格式的数据;
该方法的两个参数:

  • JPEGSampleBuffer:包含JPEG照片捕获结果的样本缓冲区,用于格式化输出。
  • previewPhotoSampleBuffer:包含照片捕获结果的预览分辨率版本的可选附加样本缓冲区,将作为缩略图添加到JPEG输出中。传递nil以跳过将预览图像添加到输出。
  • 返回值:包含所请求的照片捕获结果的JPEG表示的数据对象,如果无法打包样本缓冲区以进行输出,则为nil。
2. 获取DNG格式的数据
+ (NSData *)DNGPhotoDataRepresentationForRawSampleBuffer:(CMSampleBufferRef)rawSampleBuffer
previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer;

返回与指定样本缓冲区中捕获的RAW照片对应的数字负片DNG格式的数据;
该方法的两个参数:

  • rawSampleBuffer:包含RAW照片捕获结果的样本缓冲区,用于格式化输出。
  • previewPhotoSampleBuffer:包含照片捕获结果的预览分辨率版本的可选附加样本缓冲区,将作为缩略图添加到DNG输出中。传递nil以跳过将预览图像添加到输出。
  • 返回值:包含所请求的照片捕获结果的DNG表示的数据对象,如果无法打包样本缓冲区以进行输出,则为nil。

你可能感兴趣的:(捕获照片-AVCapturePhotoOutput)