在前面【iOS】自定義相機(jī)(六)拍照錄像中,我們介紹了如何使用AVCaptureStillImageOutput
進(jìn)行簡(jiǎn)單的拍照操作州邢。但是從 iOS 10 開(kāi)始狡刘,Apple 就棄用這個(gè)類并提供AVCapturePhotoOutput
用于進(jìn)行更多的拍照操作撮弧,比如拍攝動(dòng)態(tài)照片(Live Photo)。AVCapturePhotoOutput
是一個(gè)功能強(qiáng)大的類线婚,在新系統(tǒng)中也不斷有新的功能接入,比如iOS11支持雙攝和獲取深度數(shù)據(jù)盆均、iOS12支持人像模式塞弊。不過(guò),在本篇文章中主要介紹的還是他在 iOS10 中最基本的兩項(xiàng)功能拍攝靜態(tài)照片和動(dòng)態(tài)圖片。
該部分的代碼主要在SCCamera中的SCPhotoManager中
不管是拍攝靜態(tài)照片游沿,還是拍攝動(dòng)態(tài)照片饰抒,AVCapturePhotoOutput
的使用都有一個(gè)固定的形式:
- 在捕捉會(huì)話啟動(dòng)前創(chuàng)建并設(shè)置
AVCapturePhotoOutput
對(duì)象 - 創(chuàng)建并配置
AVCapturePhotoSettings
對(duì)象以選擇特定拍攝的功能和設(shè)置 - 調(diào)用
capturePhotoWithSettings:delegate:
方法進(jìn)行操作
capturePhotoWithSettings:delegate:
方法需要的兩個(gè)參數(shù)分別是拍照方式設(shè)置和拍照過(guò)程代理。拍照的配置將在下面的AVCapturePhotoSettings
中介紹诀黍,至于拍照過(guò)程的代理調(diào)用順序就穿插在后面的兩種拍攝方式的處理過(guò)程中袋坑。
注意:
- 由于
AVCapturePhotoOutput
已經(jīng)包含AVCaptureStillImageOutput
的功能,因此它們不可同時(shí)使用眯勾。- 由于動(dòng)態(tài)照片其實(shí)是一段小小的視頻枣宫,因此
AVCapturePhotoOutput
的動(dòng)態(tài)照片啟用的時(shí)候,AVCaptureMovieFileOutput
是不可用的咒精。
AVCapturePhotoSettings
AVCapturePhotoSettings
用于選擇在AVCapturePhotoOutput
中特定拍攝的功能和設(shè)置镶柱。
常用屬性:
-
format
:類似于AVCaptureStillImageOutput
的outputSettings
屬性,用于使用鍵值對(duì)配置模叙。- 例如配置視頻編碼
@{AVVideoCodecKey: AVVideoCodecJPEG}
歇拆。
- 例如配置視頻編碼
-
flashMode
:閃光燈模式 -
autoStillImageStabilizationEnabled
:自動(dòng)靜態(tài)圖片穩(wěn)定(防抖) -
highResolutionPhotoEnabled
: 指定輸出攝像頭支持的最高質(zhì)量圖像 -
livePhotoMovieFileURL
:動(dòng)態(tài)照片保存路徑
PS: 蘋果規(guī)定,一次拍照操作對(duì)應(yīng)一個(gè)
AVCapturePhotoSettings
實(shí)例范咨,因此在AVCapturePhotoSettings
中有一個(gè)uniqueID
屬性用于區(qū)分是否重用故觅。所以,每次拍照之前渠啊,我們都要按照需要重新創(chuàng)建一個(gè)AVCapturePhotoSettings
實(shí)例對(duì)象输吏。
AVCapturePhotoCaptureDelegate
在AVCaptureStillImageOutput
中我們是直接在閉包中獲取靜態(tài)照片,并沒(méi)有一個(gè)位置供我們告訴用戶當(dāng)前拍照過(guò)程替蛉。Apple 推出AVCapturePhotoCaptureDelegate
為開(kāi)發(fā)者提供提高用戶體驗(yàn)的位置贯溅。使用AVCapturePhotoOutput
拍照將會(huì)經(jīng)過(guò)以下五個(gè)步驟:
-
拍照配置的確定
willBeginCapture
-
開(kāi)始曝光
willCapturePhoto
-
曝光結(jié)束
didCapturePhoto
-
拍照結(jié)果返回
didFinishProcessingPhoto
-
拍照完成
didFinishCapture
拍攝靜態(tài)圖片
主要操作是AVCapturePhotoSettings
的創(chuàng)建與設(shè)置,預(yù)先設(shè)定好圖片編碼方式
- (void)takeStillPhoto:(AVCaptureVideoPreviewLayer*)previewLayer {
// 設(shè)置照片方向
AVCaptureConnection *connection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.supportsVideoOrientation) {
connection.videoOrientation = previewLayer.connection.videoOrientation;
}
// 創(chuàng)建 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];
}
靜態(tài)圖片獲取
在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. 后續(xù)處理
// ...
}
拍攝動(dòng)態(tài)圖片
硬件要求:iPhone 6s及以上機(jī)型
像素要求:不支持 AVCaptureSessionPresetHigh
在會(huì)話啟動(dòng)之前必須設(shè)置AVCapturePhotoOutput
的屬性livePhotoCaptureEnabled
為YES
它浅。然后在拍攝之前,AVCapturePhotoSettings
的重點(diǎn)配置屬性是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;
}
// 創(chuàng)建 AVCapturePhotoSettings
AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];
// 設(shè)置動(dòng)態(tài)圖片保存路徑
NSString *livePhotoMovieFileName = [[NSUUID UUID] UUIDString];
NSString *livePhotoMovieFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[livePhotoMovieFileName stringByAppendingPathExtension:@"mov"]];
photoSettings.livePhotoMovieFileURL = [NSURL fileURLWithPath:livePhotoMovieFilePath];
// 拍照
[self.photoOutput capturePhotoWithSettings:photoSettings delegate:self];
}
AVCapturePhotoCaptureDelegate 調(diào)用過(guò)程
-
willBeginCapture
:設(shè)置完畢 -
willCapturePhoto
:開(kāi)始曝光 -
didCapturePhoto
:結(jié)束曝光 -
didFinishProcessingPhoto
:靜態(tài)照片獲取位置 - ※
didFinishRecordingLivePhotoMovie
:結(jié)束動(dòng)態(tài)照片拍攝 - ※
didFinishProcessingLivePhotoToMovieFile
:動(dòng)態(tài)照片結(jié)果處理(獲取影片文件進(jìn)行處理) -
didFinishCaptureForResolvedSettings
:完成拍攝全過(guò)程
動(dòng)態(tài)照片獲取
Live Photo 是由一個(gè)圖片和一個(gè)小視頻組成的姐霍,即兩個(gè)文件(.jpg
+.mov
)。因此我們需要在AVCapturePhotoCaptureDelegate
中的didFinishProcessingPhoto
獲取靜態(tài)照片典唇,在didFinishProcessingLivePhotoToMovieFile
中獲取小視頻镊折。最后,我們將他們保存到相冊(cè)的時(shí)候需要一起寫(xiě)入介衔,系統(tǒng)會(huì)幫我們合成動(dòng)態(tài)圖片的恨胚。