【iOS】自定義相機(jī)(六)拍照錄像

巴黎圣母院

在完成了相機(jī)的各種設(shè)置之后,自定義相機(jī)最重要的功能就是拍照和錄像。本篇文章將給大家介紹基于AVCaptureStillImageOutputAVCaptureMovieFileOutput的拍照與錄像操作。雖然AVCaptureStillImageOutput在iOS 10 中被棄用,AVCaptureMovieFileOutput也不能與AVCaptureVideoDataOutput同時使用,但是鑒于這兩個類用起來相對比較簡便,并且可用于兼容低版本,因此還是將他的使用整理整理了。

對應(yīng)的代碼在SCCamera中的SCPhotographManager.mSCMovieFileOutManager.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è)置connectionhandler參數(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ù)覽不一樣。為了解決這些問題,在照片的處理上,我們分為四步走:

  1. 獲取照片原圖
  2. 獲取照片縮放圖
  3. 獲取照片裁剪圖
  4. 根據(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 了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容