[AVFoundation]圖像捕獲和視頻錄制

原文:AVFoundation Programming Guide

寫在前面

簡單翻譯一下AVFoundation的相關文檔,本人英語水平一般,有不對的歡迎大家留言指正。

要管理從相機或麥克風等設備的捕獲,您需要使用對象以表示輸入和輸出,并使用AVCaptureSession的實例來協(xié)調(diào)它們之間的數(shù)據(jù)流。你最少需要:

  • 一個AVCaptureDevice對象來表示輸入設備,如相機或麥克風
  • 一個AVCaptureInput的具體子類的實例,用于配置輸入設備端口
  • 一個AVCaptureOutput的具體子類的實例來管理是輸出到一個視頻文件還是靜態(tài)圖片
  • 一個AVCaptureSession實例來協(xié)調(diào)從輸入設備到輸出設備的數(shù)據(jù)流

為了給用戶展示相機正在錄制的內(nèi)容,你可以使用一個AVCaptureVideoPreviewLayer實例。
你可以配置多個輸入和輸出設備,并使用一個會話(session)來協(xié)調(diào),如圖4-1。

Figure 4-1 A single session can configure multiple inputs and outputs

對于許多應用,這就是你所需要的。然而,對于某些操作(例如,你想要監(jiān)視音頻通道中的功率電平)則需要考慮如何表示輸入設備的各種端口以及這些端口如何連接到輸出。

捕獲會話中的輸入和輸出之間的連接由AVCaptureConnection對象表示。捕獲輸入(AVCaptureInput的實例)具有一個或多個輸入端口(AVCaptureInputPort的實例)。捕獲輸出(AVCaptureOutput的實例)可以接受來自一個或多個源的數(shù)據(jù)(例如,AVCaptureMovieFileOutput對象接受視頻和音頻數(shù)據(jù))。

當您向會話添加輸入或輸出時,會話將形成所有兼容捕獲輸入端口和捕獲輸出之間的連接,如圖4-2所示。捕獲輸入和捕捉輸出之間的連接由AVCaptureConnection對象表示。

Figure 4-2 AVCaptureConnection represents a connection between an input and output

你可以使用一個捕獲連接來啟用或禁用一個給定的輸入或輸出的數(shù)據(jù)流。您還可以使用連接來監(jiān)視音頻通道中的平均和峰值功率。
注意: 媒體捕獲不支持在iOS設備上同時捕獲前置和后置攝像頭。

使用一個捕獲會話來協(xié)調(diào)數(shù)據(jù)流

一個AVCaptureSession對象是用于管理數(shù)據(jù)捕獲的中心協(xié)調(diào)對象。您使用它來協(xié)調(diào)從AV輸入設備到輸出的數(shù)據(jù)流。您將捕獲設備和輸出添加到會話,然后通過發(fā)送會話startRunning 消息來啟動數(shù)據(jù)流,并通過發(fā)送stopRunning消息來停止數(shù)據(jù)流。

AVCaptureSession *session = [[AVCaptureSession alloc] init];
// Add inputs and outputs.
[session startRunning];
配置會話

您可以在會話中使用預設來指定所需的圖像質(zhì)量和分辨率。預設是一些配置常數(shù);在某些情況下,實際配置是由設備決定的:

標識 分辨率 注釋
AVCaptureSessionPresetHigh High 最高的錄制質(zhì)量,每個設備有所不同
AVCaptureSessionPresetMedium Medium 適用于wifi分享,實際值可能會變
AVCaptureSessionPresetLow Low 適用于3G分享,實際值可能會變
AVCaptureSessionPreset640x480 640x480 VGA.
AVCaptureSessionPreset1280x720 1280x720 720p HD.
AVCaptureSessionPresetPhoto Photo 完整的照片分辨率,不支持視頻輸出。

如果你想要給媒體幀設置一個特定大小的配置,你應該在設置前檢查它是否支持,如下:

if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
    session.sessionPreset = AVCaptureSessionPreset1280x720;
} else {
    // Handle the failure.
}

如果您需要給會話設置一個比預設可能更精細的參數(shù),或者您想對正在運行的會話進行更改,則可以使用beginConfigurationcommitConfiguration方法來包圍您的更改。 beginConfigurationcommitConfiguration方法確保設備更改在一個組內(nèi)發(fā)生。調(diào)用beginConfiguration之后,您可以添加或刪除輸出,更改會話的預設屬性,或配置各個捕獲輸入或輸出屬性。在調(diào)用commitConfiguration之前,實際上沒有進行任何更改,這些配置會在一起被應用。

[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];
監(jiān)視捕獲會話狀態(tài)

捕獲會話會發(fā)出通知,你可以監(jiān)聽它們,例如,當它啟動或停止運行或中斷時。您可以注冊接收 AVCaptureSessionRuntimeErrorNotification來處理錯誤。您還可以查詢會話的running屬性,以確定其是否正在運行,以及interrupted屬性以確定是否中斷。此外,runninginterrupted的屬性都符合KVC,并且通知會發(fā)布在主線程上。

An AVCaptureDevice對象表示輸入設備

一個AVCaptureDevice對象是一個向AVCaptureSession對象提供輸入數(shù)據(jù)(如音頻或視頻)的物理捕獲設備的抽象。每個輸入設備有一個對象,例如兩個視頻輸入: 一個用于前置攝像頭,一個用于后置攝像頭,和一個用于麥克風的音頻輸入。您可以使用AVCaptureDevice的類方法devicesdevicesWithMediaType:找出當前可用的捕獲設備。并且,如有必要,您可以找到iPhone,iPad或iPod提供的功能(請參閱Device Capture Settings)??捎迷O備的列表可能會改變。當前的輸入設備可能變得不可用(如果它們被另一個應用程序使用),并且新的輸入設備可能變得可用(如果它們被另一個應用程序放棄)。您應該注冊接收AVCaptureDeviceWasConnectedNotificationAVCaptureDeviceWasDisconnectedNotification 通知,以便在可用設備列表更改時提醒。您可以使用捕獲輸入將輸入設備添加到捕獲會話(請參閱Use Capture Inputs to Add a Capture Device to a Session)。

設備特性

您可以查詢設備的不同的特征。您還可以測試它是否提供特定的媒體類型或支持給定捕獲會話的預設,分別使用方法hasMediaType:supportsAVCaptureSessionPreset:。要向用戶提供信息,您可以找出捕獲設備的位置(無論是在正在測試的設備的正面或背面)及其本地化名稱。如果要提供捕獲設備列表以允許用戶選擇一個捕獲設備,這可能很有用。

圖4-3顯示了后置(AVCaptureDevicePositionBack) 和前置(AVCaptureDevicePositionFront)攝像機的位置。

注意:媒體捕獲不支持同時捕獲iOS設備上的前置和后置攝像頭。

Figure 4-3 iOS device front and back facing camera positions

下面的代碼展示了獲取所有的可用設備并且輸出它們的名字,如果是視頻設備,輸出它們的位置

NSArray *devices = [AVCaptureDevice devices];

for (AVCaptureDevice *device in devices) {
    NSLog(@"Device name: %@", [device localizedName]);
    if ([device hasMediaType:AVMediaTypeVideo]) {
        if ([device position] == AVCaptureDevicePositionBack) {
            NSLog(@"Device position : back");
        } else {
            NSLog(@"Device position : front");
        }
    }
}

你還可以獲取設備的model ID和unique ID。

設備捕獲設置

不同的設備具有不同的功能;例如,一些可能支持不同的焦點或閃光模式;有一些可能會支持在一個興趣點上聚焦。
以下代碼片段顯示了如何查找具有手電筒模式并支持給定捕獲會話預置的視頻輸入設備:

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];

NSMutableArray *torchDevices = [[NSMutableArray alloc] init];

for (AVCaptureDevice *device in devices) {
    if ([device hasTorch] && [device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
        [torchDevices addObject:device];
    }
}

如果您發(fā)現(xiàn)多個符合條件的設備,您可以讓用戶選擇要使用的設備。要向用戶顯示設備的描述,可以使用其localizedName屬性。

您以類似的方式使用各種不同的功能。有一些常數(shù)來指定特定模式,您可以詢問設備是否支持特定模式。在某些情況下,您可以監(jiān)聽屬性以便在功能正在更改時獲得通知。在所有情況下,您應該在更改特定功能的模式之前鎖定設備,如Configuring a Device中所述。

注意:興趣點聚焦和興趣點曝光是相互排斥的,聚焦模式和曝光模式也是相互排斥的。

聚焦模式

有三種聚焦模式:

  • AVCaptureFocusModeLocked: 焦點位置是固定的。當您想允許用戶組合場景然后鎖定焦點時,這很有用。
  • AVCaptureFocusModeAutoFocus: 相機執(zhí)行一個掃描焦點,然后還原到鎖定。這適合于您想要選擇要聚焦的特定項目,然后將焦點保持在該項目上的情況,即使它不是場景的中心。
  • AVCaptureFocusModeContinuousAutoFocus: 相機會根據(jù)需要不斷自動對焦。

您使用isFocusModeSupported:方法來確定設備是否支持給定的對焦模式,然后使用focusMode屬性設置模式。
另外,設備可能支持興趣焦點。 您可以使用focusPointOfInterest來測試。 如果支持,您可以使用focusPointOfInterest設置焦點。 您傳遞一個CGPoint,其中{0,0}表示圖片區(qū)域的左上角,而{1,1}表示右下角在橫向模式(Home鍵在右側(cè)),即使設備處于縱向模式時也是。

您可以使用adjustingFocus屬性來確定設備當前是否正在進行對焦。 您可以使用鍵值觀察觀察屬性,以在設備啟動和停止對焦時獲得通知。

如果更改對焦模式設置,可以按如下方式將其返回到默認配置:

if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {

    CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f);

    [currentDevice setFocusPointOfInterest:autofocusPoint];

    [currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
曝光模式

有兩種曝光模式:

您使用isExposureModeSupported:方法來確定設備是否支持給定的曝光模式,然后使用exposureMode屬性設置模式。

另外,設備可能支持興趣點曝光。 您可以使用exposurePointOfInterestSupported測試。 如果支持,您可以使用exposurePointOfInterest設置曝光點。 您傳遞一個CGPoint,其中{0,0}表示圖片區(qū)域的左上角,而{1,1}表示右下角在橫向模式(Home鍵在右側(cè)),即使設備處于縱向模式時也是。

您可以使用adjustingExposure屬性來確定設備是否正在更改其曝光設置。 您可以使用鍵值觀察監(jiān)聽屬性,以在設備啟動或停止改變曝光模式時獲得通知。

如果更改曝光設置,可以按如下方式將其返回到默認配置:

if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {

    CGPoint exposurePoint = CGPointMake(0.5f, 0.5f);

    [currentDevice setExposurePointOfInterest:exposurePoint];

    [currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
閃光模式

有三種閃光模式:

您使用hasFlash來確定設備是否有閃光燈。 如果該方法返回YES,則使用isFlashModeSupported:方法,傳遞所需的模式以確定設備是否支持給定的閃光模式,然后使用flashMode屬性設置模式。

手電筒模式

在手電筒模式下,閃光燈以低功率持續(xù)照明來進行視頻拍攝。有三種手電筒模式:

您使用hasTorch來確定設備是否有閃關燈。 您使用isTorchModeSupported:方法來確定設備是否支持給定的閃關燈模式,然后使用torchMode屬性設置模式。

對于帶手電筒的設備,手電筒只有在設備與正在運行的捕獲會話關聯(lián)時才會打開。

視頻穩(wěn)定

視頻穩(wěn)定是否可用于操作視頻取決于特定的設備硬件。 即使如此,并不是所有的源格式和視頻分辨率都得到支持。

啟用電影視頻穩(wěn)定也可能在視頻捕獲管道中加入額外的延遲。 要檢測視頻穩(wěn)定是否正在使用,請使用videoStabilizationEnabled屬性。 enablesVideoStabilizationWhenAvailable屬性允許應用程序自動啟用視頻穩(wěn)定(如果相機支持)。 默認情況下,由于上述限制,自動穩(wěn)定功能被禁用。

白平衡

有兩種白平衡模式:

您使用isWhiteBalanceModeSupported:方法來確定設備是否支持給定的白平衡模式,然后使用whiteBalanceMode屬性設置模式。

您可以使用adjustingWhiteBalance屬性來確定設備當前是否正在更改其白平衡設置。 您可以使用鍵值觀察觀察屬性,以在設備啟動或停止更改其白平衡設置時獲得通知。

設置設備方向

您可以在AVCaptureConnection上設置所需的方向,以指定如何在AVCaptureOutput(AVCaptureMovieFileOutput,AVCaptureStillImageOutput和AVCaptureVideoDataOutput)中為圖形定向。

使用AVCaptureConnectionsupportsVideoOrientation屬性來確定設備是否支持更改視頻的方向,以及videoOrientation屬性來指定在輸出端口中的圖像的方向。 List 4-1顯示了如何將AVCaptureConnection的方向設置為AVCaptureVideoOrientationLandscapeLeft

List 4-1設置捕獲連接的方向

AVCaptureConnection *captureConnection = <#A capture connection#>;

if ([captureConnection isVideoOrientationSupported]) {

      AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;

     [captureConnection setVideoOrientation:orientation];
}
配置設備

要在設備上設置捕獲屬性,必須先使用lockForConfiguration:獲取設備的鎖定。 這樣可以避免與其他應用程序中的設置不兼容的更改。 以下代碼片段說明了如何通過首先確定是否支持該模式,然后嘗試鎖定設備以進行重新配置來改變設備上的聚焦模式。 僅當獲得鎖定時才改變對焦模式,然后立即釋放鎖定。

if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {

    NSError *error = nil;

    if ([device lockForConfiguration:&error]) {

        device.focusMode = AVCaptureFocusModeLocked;

        [device unlockForConfiguration];

    } else {
        // Respond to the failure as appropriate.

只有當您需要可設置的設備屬性保持不變時,才應該保持設備鎖定。 不必要地持有設備鎖會降低共享設備的其他應用程序的捕獲效果。

在設備之間切換

有時您可能希望允許用戶在輸入設備之間切換,例如從使用前置攝像頭切換到后置攝像頭。 為了避免停頓或卡頓,您可以在運行時重新配置會話,但是您應該使用beginConfigurationcommitConfiguration來包含您的配置更改:

AVCaptureSession *session = <#A capture session#>;

[session beginConfiguration];

[session removeInput:frontFacingCameraDeviceInput];

[session addInput:backFacingCameraDeviceInput];

[session commitConfiguration];

當最外面的配置被調(diào)用時,所有的更改都在一起被改變。 這確保了一個平穩(wěn)過渡。

使用捕獲輸入將捕獲設備添加到會話

要將捕獲設備添加到捕獲會話中,可以使用AVCaptureDeviceInput(抽象類AVCaptureInput的具體子類)的實例。 捕獲設備輸入管理設備的端口。

NSError *error;

AVCaptureDeviceInput *input =

[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

if (!input) {

// Handle the error appropriately.

}

您可以使用addInput:向會話添加輸入。 您可以使用canAddInput:檢查捕獲輸入是否與現(xiàn)有會話兼容。

AVCaptureSession *captureSession = <#Get a capture session#>;

AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>;

if ([captureSession canAddInput:captureDeviceInput]) {
    [captureSession addInput:captureDeviceInput];
} else {
    // Handle the failure.
}

有關如何重新配置正在運行的會話的詳細信息,請參閱Configuring a Session 。
AVCaptureInput會提供一個或多個媒體數(shù)據(jù)流。 例如,輸入設備可以提供音頻和視頻數(shù)據(jù)。 輸入提供的每個媒體流由AVCaptureInputPort對象表示。 捕獲會話使用AVCaptureConnection對象來定義一組AVCaptureInputPort對象與單個AVCaptureOutput之間的映射。

使用捕獲輸出從會話獲取輸出

要從捕獲會話獲取輸出,您需要添加一個或多個輸出。 輸出是AVCaptureOutput的具體子類的一個實例。 你可以使用:

您可以使用addOutput:將輸出添加到捕獲會話。 您可以使用canAddOutput:檢查捕獲輸出是否與現(xiàn)有會話兼容。 您可以在會話運行時根據(jù)需要添加和刪除輸出。

AVCaptureSession *captureSession = <#Get a capture session#>;

AVCaptureMovieFileOutput *movieOutput = <#Create and configure a movie output#>;

if ([captureSession canAddOutput:movieOutput]) {
    [captureSession addOutput:movieOutput];
} else {
    // Handle the failure.
}
保存到電影文件

可以使用AVCaptureMovieFileOutput對象將電影數(shù)據(jù)保存到文件。(AVCaptureMovieFileOutputAVCaptureFileOutput的具體子類,它定義了大部分的基本行為。)您可以配置影片文件輸出的各個方面,例如錄制的最長持續(xù)時間或文件的最大尺寸。 如果剩余的磁盤空間少于一定數(shù)量,您也可以禁止錄制。

AVCaptureMovieFileOutput *aMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];

CMTime maxDuration = <#Create a CMTime to represent the maximum duration#>;

aMovieFileOutput.maxRecordedDuration = maxDuration;

aMovieFileOutput.minFreeDiskSpaceLimit = <#An appropriate minimum given the quality of the movie format and the duration#>;

輸出的分辨率和比特率取決于捕獲會話的sessionPreset。 視頻編碼通常是H.264,音頻編碼通常是AAC。 實際值因設備而異。

開始錄制

您可以使用startRecordingToOutputFileURL:recordingDelegate:來錄制QuickTime影片。您需要提供一個基于文件的URL和代理。 URL不能標識現(xiàn)有文件,因為電影文件輸出不會覆蓋現(xiàn)有資源。 您還必須具有寫入指定位置的權限。 代理必須符合AVCaptureFileOutputRecordingDelegate協(xié)議,并且必須實現(xiàn)captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:方法。

AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;

NSURL *fileURL = <#A file URL that identifies the output location#>;

[aMovieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:<#The delegate#>];

在實現(xiàn)captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:時,代理可能會將生成的電影寫入Camera Roll相冊。 它還應該檢查可能發(fā)生的任何錯誤。

確保文件成功寫入

要確定文件是否成功保存,在實現(xiàn)captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:時,您不僅要檢查錯誤,還檢查錯誤的用戶信息字典(error’s user info dictionary)中的AVErrorRecordingSuccessfullyFinishedKey的值:

- (void)captureOutput:(AVCaptureFileOutput *)captureOutput 
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
    BOOL recordedSuccessfully = YES;
    
    if ([error code] != noErr) {
    // A problem occurred: Find out if the recording was successful.

    id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];

    if (value) {
        recordedSuccessfully = [value boolValue];
    }
  }
  // Continue as appropriate...

您應該檢查AVErrorRecordingSuccessfullyFinishedKeykey的值,因為該文件可能已成功保存,即使返回了一個錯誤。 錯誤可能表示您的錄制達到了一個約束 - 例如,AVErrorMaximumDurationReached或者AVErrorMaximumFileSizeReached。 其他導致錄制結束的原因是:

添加元數(shù)據(jù)到文件

你也可以隨時設置影片文件的元數(shù)據(jù),即使在錄制時。 對于在記錄開始時信息不可用的情況(如位置信息一樣)下這是很有用的。 文件輸出的元數(shù)據(jù)由AVMetadataItem對象的數(shù)組表示; 您可以使用其可變子類AVMutableMetadataItem的實例來創(chuàng)建您自己的元數(shù)據(jù)。

AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;

NSArray *existingMetadataArray = aMovieFileOutput.metadata;

NSMutableArray *newMetadataArray = nil;

if (existingMetadataArray) {
    newMetadataArray = [existingMetadataArray mutableCopy];
} else {
    newMetadataArray = [[NSMutableArray alloc] init];
}

AVMutableMetadataItem *item = [[AVMutableMetadataItem alloc] init];

item.keySpace = AVMetadataKeySpaceCommon;

item.key = AVMetadataCommonKeyLocation;

CLLocation *location - <#The location to set#>;

item.value = [NSString stringWithFormat:@"%+08.4lf%+09.4lf/" location.coordinate.latitude, location.coordinate.longitude];

[newMetadataArray addObject:item];

aMovieFileOutput.metadata = newMetadataArray;
處理視頻幀

AVCaptureVideoDataOutput對象使用委托來公開視頻幀。您可以使用setSampleBufferDelegate:queue:設置委托。除了設置委托,您還可以指定一個在其上調(diào)用委托方法的串行隊列。您必須使用串行隊列來確保視頻幀以適當?shù)捻樞騻鬟f給代理。您可以使用隊列來修改傳遞的優(yōu)先級和處理視頻幀。有關示例實現(xiàn),請參閱SquareCam

這些視頻幀在委托方法captureOutput:didOutputSampleBuffer:fromConnection:中,作為CMSampleBufferRef的實例(請參閱 Representations of Media)。默認情況下,緩沖區(qū)以相機最有效的格式發(fā)出。您可以使用videoSettings屬性來指定自定義輸出格式。視頻設置屬性是一個字典;目前唯一支持的鍵是kCVPixelBufferPixelFormatTypeKey。推薦的像素格式由availableVideoCVPixelFormatTypes屬性返回,由availableVideoCodecTypes屬性返回支持的值。 Core GraphicsOpenGL都能與BGRA格式配合使用:

AVCaptureVideoDataOutput *videoDataOutput = [AVCaptureVideoDataOutput new];

NSDictionary *newSettings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };

videoDataOutput.videoSettings = newSettings;

// discard if the data output queue is blocked (as we process the still image
[videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];)

// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
// see the header doc for setSampleBufferDelegate:queue: for more information

videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);

[videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];

AVCaptureSession *captureSession = <#The Capture Session#>;

if ( [captureSession canAddOutput:videoDataOutput] )
    [captureSession addOutput:videoDataOutput];
處理視頻的性能注意事項

您應該將會話輸出設置為應用程序的最低實際分辨率。將輸出設置為更高的分辨率會浪費處理周期,并且耗電。

您必須確保實現(xiàn)方法captureOutput:didOutputSampleBuffer:fromConnection:能夠在分配給幀的時間量內(nèi)處理樣本緩沖區(qū)。如果時間太長,并且您持有了視頻幀,AV Foundation會停止傳輸視頻幀,不僅是你的代理,h還包括其他的輸出(如預覽圖層)。

您可以使用捕獲視頻數(shù)據(jù)輸出的minFrameDuration屬性來確保您有足夠的時間來處理幀,而不是以比其他情況更低的幀速率。您還應該確保將alwaysDiscardsLateVideoFrames屬性設置為YES(默認值)。這樣可以確保任何延遲的視頻幀都被丟棄,而不是交給您進行處理?;蛘?,如果不在乎錄制時的延遲,并且您希望獲得所有的數(shù)據(jù),您可以將屬性值設置為NO。這并不意味著幀不會被丟棄(也就是說,幀可能會被丟棄),但是它們可能不會被過早或者頻繁地被丟棄。

捕捉靜態(tài)圖像

如果要捕獲靜態(tài)圖像,可以使用AVCaptureStillImageOutput輸出。 圖像的分辨率取決于會話的預設以及設備。

像素和編碼格式

不同的設備支持不同的圖像格式。 您可以分別使用availableImageDataCVPixelFormatTypesavailableImageDataCodecTypes找到設備支持的像素和編碼類型。 每個方法返回設備的支持的值得一個數(shù)組。 您設置outputSettings字典以指定所需的圖像格式,例如:

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG};
[stillImageOutput setOutputSettings:outputSettings];

如果要捕獲JPEG圖像,則通常不應指定您自己的壓縮格式。 相反,您應該讓靜態(tài)圖像輸出為您做壓縮,因為它的壓縮是硬件加速的。 如果您需要圖像的數(shù)據(jù)表示,您可以使用jpegStillImageNSDataRepresentation:獲取NSData對象而不重新壓縮數(shù)據(jù),即使您修改了圖像的元數(shù)據(jù)。

捕獲圖像

當您要捕獲圖像時,您可以給輸出發(fā)送一個captureStillImageAsynchronouslyFromConnection:completionHandler:消息。 第一個參數(shù)是要用于捕獲的連接。 您需要查找正在收集視頻的輸入端口的連接:

AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections) {
    for (AVCaptureInputPort *port in [connection inputPorts]) {
        if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
            videoConnection = connection;
            break;
        }
    }
    if (videoConnection) { break; }
}

captureStillImageAsynchronouslyFromConnection:completionHandler:的第二個參數(shù)是一個包含兩個參數(shù)的block:一個包含圖像數(shù)據(jù)的CMSampleBuffer和一個錯誤。 樣本緩沖區(qū)本身可以包含諸如EXIF字典的元數(shù)據(jù)作為附件。 您可以根據(jù)需要修改附件,但請注意 Pixel and Encoding Formats中討論的JPEG圖像的優(yōu)化。

[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:
    ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
        CFDictionaryRef exifAttachments =
            CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
        if (exifAttachments) {
            // Do something with the attachments.
        }
        // Continue as appropriate.
    }];

給用戶展示正在錄制的內(nèi)容

您可以向用戶展示相機(使用預覽圖)或麥克風(通過監(jiān)聽音頻通道)正在錄制的內(nèi)容。

視頻預覽

您可以使用AVCaptureVideoPreviewLayer對象向用戶展示正在錄制的內(nèi)容。 AVCaptureVideoPreviewLayerCALayer的子類(請參閱Core Animation Programming Guide),您不需要任何輸出來顯示預覽。
使用AVCaptureVideoDataOutput類為客戶端應用程序提供了在呈現(xiàn)給用戶之前訪問視頻像素的能力。
與捕獲輸出不同,視頻預覽層持有了與其相關聯(lián)的會話的強引用。 這是為了確保在預覽層嘗試顯示視頻時會話不會被釋放。下面是初始化預覽圖層的方法:

AVCaptureSession *captureSession = <#Get a capture session#>;
CALayer *viewLayer = <#Get a layer from the view in which you want to present the preview#>;
 
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
[viewLayer addSublayer:captureVideoPreviewLayer];

通常,預覽圖層與其他CALayer對象相同(參見Core Animation Programming Guide)。 您可以像使用其他圖層一樣縮放圖像、執(zhí)行轉(zhuǎn)換、旋轉(zhuǎn)等操作。 一個區(qū)別是,您可能需要設置圖層的orientation屬性來指定如何旋轉(zhuǎn)來自相機的圖像。 另外,您可以通過查詢supportsVideoMirroring屬性來測試設備是否支持視頻鏡像。 您可以根據(jù)需要設置videoMirrored屬性,但是當將automaticallyAdjustsVideoMirroring屬性設置為YES(默認值)時,將根據(jù)會話的配置自動設置鏡像值。

視頻重力模式

預覽圖層支持三種重力模式,可以使用videoGravity設置:

在預覽中使用“點擊聚焦”

在預覽圖層中實現(xiàn)點擊對焦時,您需要注意。 您必須考慮圖層的預覽方向和重力,以及預覽展示的可能性。 請參閱示例項目AVCam-iOS: Using AVFoundation to Capture Images and Movies。

顯示音頻音量

要監(jiān)視拍攝連接中音頻通道中的平均和峰值級別,可以使用AVCaptureAudioChannel對象。 音頻音量不支持鍵值觀察,所以您必須按您想要的頻率輪詢更新用戶界面(例如,每秒10次)。

AVCaptureAudioDataOutput *audioDataOutput = <#Get the audio data output#>;
NSArray *connections = audioDataOutput.connections;
if ([connections count] > 0) {
    // There should be only one connection to an AVCaptureAudioDataOutput.
    AVCaptureConnection *connection = [connections objectAtIndex:0];
 
    NSArray *audioChannels = connection.audioChannels;
 
    for (AVCaptureAudioChannel *channel in audioChannels) {
        float avg = channel.averagePowerLevel;
        float peak = channel.peakHoldLevel;
        // Update the level meter user interface.
    }
}

整合:將視頻幀捕獲為UIImage對象

這個簡短的代碼示例說明了如何捕獲視頻并將獲得的幀轉(zhuǎn)換為UIImage對象。:

注意:為了專注于最相關的代碼,本示例省略了完整應用程序的幾個方面,包括內(nèi)存管理。 要使用AVFoundation,您需要有足夠的Cocoa編程經(jīng)驗,以推斷出丟失的部分。

創(chuàng)建并配置捕獲會話

您可以使用AVCaptureSession對象來協(xié)調(diào)從AV輸入到輸出的數(shù)據(jù)流。 創(chuàng)建會話,并將其配置為生成中等分辨率的視頻幀。

AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;
創(chuàng)建和配置設備以及設備輸入

捕獲設備由AVCaptureDevice對象表示; 該類提供了檢索所需輸入類型的對象的方法。 一個設備具有一個或多個端口,可以使用AVCaptureInput對象進行配置。 通常,你可以使用默認的配置。
查找視頻捕獲設備,然后使用設備創(chuàng)建設備輸入并將其添加到會話中。 如果找不到適當?shù)脑O備,則deviceInputWithDevice:error:方法將通過引用返回錯誤。

AVCaptureDevice *device =
        [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
 
NSError *error = nil;
AVCaptureDeviceInput *input =
        [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
    // Handle the error appropriately.
}
[session addInput:input];
創(chuàng)建和配置視頻數(shù)據(jù)輸出

你可以使用AVCaptureVideoDataOutput對象來處理被捕獲的視頻中的未壓縮幀。 您通常會配置輸出的幾個方面。 例如,對于視頻,您可以使用videoSettings屬性指定像素格式,并通過設置minFrameDuration屬性來限制幀率。

創(chuàng)建并配置視頻數(shù)據(jù)的輸出并將其添加到會話中; 通過把minFrameDuration屬性設置為1/15秒將幀率設置為15 fps:

AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:output];
output.videoSettings =
                @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
output.minFrameDuration = CMTimeMake(1, 15);

數(shù)據(jù)輸出對象使用委托來處理視頻幀。 代理必須實現(xiàn)AVCaptureVideoDataOutputSampleBufferDelegate協(xié)議。 當你設置數(shù)據(jù)輸出的委托時,還必須提供一個隊列,將在該隊列上調(diào)用回調(diào)函數(shù)。

dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);

你使用隊列來修改發(fā)送和處理視頻幀的優(yōu)先級。

實現(xiàn)Sample Buffer代理方法

在委托類中,實現(xiàn)captureOutput:didOutputSampleBuffer:fromConnection:
方法,它會在樣本緩沖區(qū)被寫入時調(diào)用。 視頻數(shù)據(jù)輸出對象將幀作為CMSampleBuffer類型傳輸,因此您需要將CMSampleBuffer類型轉(zhuǎn)換為UIImage對象。 可以參考 Converting CMSampleBuffer to a UIImage Object

- (void)captureOutput:(AVCaptureOutput *)captureOutput
         didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
         fromConnection:(AVCaptureConnection *)connection {
 
    UIImage *image = imageFromSampleBuffer(sampleBuffer);
    // Add your code here that uses the image.
}

記住調(diào)用代理方法會在setSampleBufferDelegate:queue:中指定的隊列上調(diào)用; 如果要更新用戶界面,則必須在主線程上調(diào)用相關的代碼。

開始以及結束錄制

配置捕獲會話后,您應確保有錄制的權限。

NSString *mediaType = AVMediaTypeVideo;
 
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
    if (granted)
    {
        //Granted access to mediaType
        [self setDeviceAuthorized:YES];
    }
    else
    {
        //Not granted access to mediaType
        dispatch_async(dispatch_get_main_queue(), ^{
        [[[UIAlertView alloc] initWithTitle:@"AVCam!"
                                    message:@"AVCam doesn't have permission to use Camera, please change privacy settings"
                                   delegate:self
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil] show];
                [self setDeviceAuthorized:NO];
        });
    }
}];

如果配置好了會話,并且用戶已經(jīng)允許訪問攝像機(如果需要,麥克風),就可以發(fā)送startRunning消息開始錄制。

重要提示startRunning方法是一個阻塞調(diào)用,可能需要一些時間,因此您應該在串行隊列上執(zhí)行會話設置,以使主隊列不被阻塞。 請參閱AVCam-iOS: Using AVFoundation to Capture Images and Movies

[session startRunning];

結束錄制可以發(fā)送stopRunning消息。

高幀率視頻采集

iOS 7.0在所選硬件上引入了對高幀率視頻捕獲的支持(也稱為慢動作視頻)。AVFoundation框架支持高幀率內(nèi)容。

您可以使用AVCaptureDeviceFormat類來確定設備的捕獲功能。此類具有返回支持的媒體類型、幀率、視場、最大縮放系數(shù)、是否支持視頻穩(wěn)定等的方法。

捕獲支持720p(1280 x 720像素)分辨率以每秒60幀(fps)的幀率,包括視頻穩(wěn)定和可放置的P幀(H264編碼視頻的功能,使在較慢和較舊的硬件上也能讓視頻播放順暢。 )

播放增強了對慢速和快速播放音頻的支持,從而可以以更慢或更快的速度播放音頻。

編輯完全支持在可變組件中的縮放編輯。

導出提供兩個選項,支持60 fps視頻??捎玫膸剩倩蚩焖龠\動,或者將影片轉(zhuǎn)換為任意較慢的幀速率,例如每秒30幀。

SloPoke(沒找到=。=)示例代碼演示了AVFoundation支持快速視頻捕獲,確定硬件是否支持高幀速率視頻捕獲,使用各種速率和時間間距算法的播放以及編輯(包括為部分組件設置時間刻度)。

回放

AVPlayer的實例通過setRate:方法自動管理大部分播放速度。該值用作播放速度的乘數(shù)。值為1.0會導致正常播放,0.5以半速播放,5.0會使播放比正??煳灞叮来祟愅?。

AVPlayerItem對象支持audioTimePitchAlgorithm屬性。該屬性允許您指定使用 Time Pitch Algorithm Settings常數(shù)以各種幀速率播放動畫時的音頻播放方式。

下表展示了支持的時間間距算法(Time pitch algorithm),質(zhì)量(Quality),算法是否使音頻捕捉到特定幀速率(Snaps to specific frame rate),以及每個算法支持的幀速率范圍(Rate range)。

Time pitch algorithm Quality Snaps to specific frame rate Rate range
AVAudioTimePitchAlgorithmLowQualityZeroLatency 質(zhì)量低,適合快進,倒帶或低質(zhì)量的聲音 YES 0.5, 0.666667, 0.8, 1.0, 1.25, 1.5, 2.0 rates.
AVAudioTimePitchAlgorithmTimeDomain 質(zhì)量適中,計算量少,適合聲音。 NO 0.5–2x rates.
AVAudioTimePitchAlgorithmSpectral 最高質(zhì)量,計算量大,保留原始項目的間距。 NO 1/32–32 rates.
AVAudioTimePitchAlgorithmVarispeed 高品質(zhì)播放,無間距校正。 NO 1/32–32 rates.
編輯

編輯時,您可以使用AVMutableComposition類來創(chuàng)建時間編輯。

導出

導出60 fps視頻時使用AVAssetExportPresetPassthrough類導出資源。可以使用兩種技術導出內(nèi)容:

  • 使用AVAssetExportPresetPassthrough預設來避免重新編碼影片。媒體將媒體部分標記為60 fps,部分放慢或部分加快。
  • 使用恒定的幀速率導出以實現(xiàn)最大播放兼容性。將視頻組件的frameDuration屬性設置為30 fps。您還可以通過使用導出會話的audioTimePitchAlgorithm屬性來設置時間間隔。
錄制

您可以使用AVCaptureMovieFileOutput類捕獲高幀率視頻,該類自動支持高幀率錄制。它將自動選擇正確的H264間距和比特率。

要進行自定義錄制,您必須使用AVAssetWriter類,這需要一些額外的設置。

assetWriterInput.expectsMediaDataInRealTime=YES;

此設置確保捕獲可以跟上輸入的數(shù)據(jù)。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容