
在完成了相機(jī)的各種設(shè)置之后,自定義相機(jī)最重要的功能就是拍照和錄像。本篇文章將給大家介紹基于AVCaptureStillImageOutput和AVCaptureMovieFileOutput的拍照與錄像操作。雖然AVCaptureStillImageOutput在iOS 10 中被棄用,AVCaptureMovieFileOutput也不能與AVCaptureVideoDataOutput同時使用,但是鑒于這兩個類用起來相對比較簡便,并且可用于兼容低版本,因此還是將他的使用整理整理了。
對應(yīng)的代碼在SCCamera中的SCPhotographManager.m和SCMovieFileOutManager.m中。
拍照操作
首先需要創(chuàng)建AVCaptureStillImageOutput對象,并設(shè)置outputSettings,然后再添加進(jìn)當(dāng)前使用的捕捉會話中。
AVCaptureStillImageOutput *stillImageOutput = [AVCaptureStillImageOutput new];
stillImageOutput.outputSettings = @{AVVideoCodecKey: AVVideoCodecJPEG};
if ([self.session canAddOutput:stillImageOutput]) {
[self.session addOutput:stillImageOutput];
}
需要拍照的時候調(diào)用captureStillImageAsynchronouslyFromConnection:方法即可,完整聲明如下:
- (void)captureStillImageAsynchronouslyFromConnection:(AVCaptureConnection *)connection completionHandler:(void (^)(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error))handler;
該方法需要的參數(shù)如下:
-
connection:視頻連接 -
handler:異步處理 Block,即獲取拍照片的途徑
下面將拍照操作分成拍照前和拍照后兩部分進(jìn)行介紹:
拍照前操作
拍照之前重要的操作就是獲取設(shè)置connection和handler參數(shù),在設(shè)置參數(shù)之前,我們可以通過以下方法獲取connection并進(jìn)行設(shè)置:
// 1. 獲取 stillImageConnection
AVCaptureConnection* stillImageConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
// 2. 設(shè)置 stillImageConnection
if (stillImageConnection.supportsVideoOrientation) {
stillImageConnection.videoOrientation = videoOrientation;
}
拍照后操作
handler中的操作就是拍照之后我們對圖片處理的操作,他的類型是void (^)(CMSampleBufferRef, NSError *)。這里會有以下兩個問題:1. 沒看到熟悉的UIImage對象;2. 我們的預(yù)覽視圖可能設(shè)置成了AVLayerVideoGravityResizeAspectFill模式,拍出來的照片肯定和用戶看到的預(yù)覽不一樣。為了解決這些問題,在照片的處理上,我們分為四步走:
- 獲取照片原圖
- 獲取照片縮放圖
- 獲取照片裁剪圖
- 根據(jù)需求進(jìn)行后續(xù)操作
void (^completionHandler)(CMSampleBufferRef, NSError *) = ^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error){
if (!imageDataSampleBuffer || error) {
// TODO: - 處理錯誤
return;
}
// 1. 獲取 originImage
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *originImage = [[UIImage alloc] initWithData:imageData];
// 統(tǒng)一修改圖片方向
originImage = [originImage fixOrientation];
// 2. 獲取 scaledImage
CGFloat width = previewLayer.bounds.size.width;
CGFloat height = previewLayer.bounds.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. 根據(jù)需求進(jìn)行后續(xù)操作
// TODO: - 業(yè)務(wù)操作
};
這里的圖片處理分別是先將圖片縮放,再進(jìn)行裁剪。這個過程其實(shí)就在模仿預(yù)覽視圖中
ResizeAspectFill的操作,最后的croppedImage就和預(yù)覽視圖的一模一樣了。(中間用到的UIImage方法具體見本項(xiàng)目中的UIImage+SCCamera.m)
最后別忘了調(diào)用拍照方法:
[stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler: completionHandler];
錄像操作
首先需要創(chuàng)建AVCaptureMovieFileOutput對象,然后再添加進(jìn)當(dāng)前使用的捕捉會話中。
self.movieFileOutput = [AVCaptureMovieFileOutput new];
if ([self.session canAddOutput:movieFileOutput]) {
[self.session addOutput:movieFileOutput];
}
錄制視頻和拍照不同,他有一個錄制開始和錄制結(jié)束,我們可以直接調(diào)用AVCaptureMovieFileOutput的開始錄制方法和錄制結(jié)束方法進(jìn)行操作。根據(jù)要求,我們還需要實(shí)現(xiàn)一個AVCaptureFileOutputRecordingDelegate代理進(jìn)行錄制狀態(tài)的監(jiān)聽與處理,當(dāng)然錄制結(jié)束后,我們也需要在代理方法里面獲取視頻信息。我將這個錄制過程分為下面四步:
第一步:生成文件路徑
self.movieURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"movie.mov"]];
該路徑用于錄制過程中臨時視頻的保存,十分重要,需要使用強(qiáng)引用。
第二步:開始錄像處理
- (void)start:(AVCaptureVideoOrientation)orientation {
if (!self.movieFileOutput.isRecording) {
AVCaptureConnection *videoConnection = [self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
if (videoConnection.supportsVideoStabilization) {
videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
if (videoConnection.supportsVideoOrientation) {
videoConnection.videoOrientation = orientation;
}
[self.movieFileOutput startRecordingToOutputFileURL:self.movieURL recordingDelegate:self];
}
}
AVCaptureVideoStabilizationModeAuto表示自動進(jìn)行視頻穩(wěn)定錄制
第三步:結(jié)束錄像處理
- (void)stop{
if (self.movieFileOutput.isRecording) {
[self.movieFileOutput stopRecording];
}
}
第四步:錄像完成處理
- (void)captureOutput:(nonnull AVCaptureFileOutput *)output didFinishRecordingToOutputFileAtURL:(nonnull NSURL *)outputFileURL fromConnections:(nonnull NSArray<AVCaptureConnection *> *)connections error:(nullable NSError *)error {
// 拿到 outputFileURL 進(jìn)行后續(xù)操作
}
在AVCaptureFileOutputRecordingDelegate中除了有上面第四步的用途,還可以處理錄像被打斷等多種情況。
補(bǔ)充
關(guān)于 videoOrientation
在拍照和錄像中都用到了videoOrientation,該屬性是AVCaptureVideoOrientation,共有四個方向:
-
AVCaptureVideoOrientationPortrait:豎直方向,HOME鍵在下面 -
AVCaptureVideoOrientationPortraitUpsideDown:豎直方向,HOME鍵在上面 -
AVCaptureVideoOrientationLandscapeRight:水平方向,HOME鍵在右邊 -
AVCaptureVideoOrientationLandscapeLeft:水平方向,HOME鍵在左邊
視頻流是不知道我們需要什么方向的視頻的,需要我們在連接(connection)中設(shè)置,剩下的方向轉(zhuǎn)換工作就交給 AVFoundation 了。