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

原文:AVFoundation Programming Guide

寫(xiě)在前面

簡(jiǎn)單翻譯一下AVFoundation的相關(guān)文檔,本人英語(yǔ)水平一般,有不對(duì)的歡迎大家留言指正。

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

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

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

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

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

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

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

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

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

使用一個(gè)捕獲會(huì)話來(lái)協(xié)調(diào)數(shù)據(jù)流

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

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

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

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

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

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

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

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

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

An AVCaptureDevice對(duì)象表示輸入設(shè)備

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

設(shè)備特性

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

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

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

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

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

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");
        }
    }
}

你還可以獲取設(shè)備的model ID和unique ID。

設(shè)備捕獲設(shè)置

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

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)多個(gè)符合條件的設(shè)備,您可以讓用戶選擇要使用的設(shè)備。要向用戶顯示設(shè)備的描述,可以使用其localizedName屬性。

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

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

聚焦模式

有三種聚焦模式:

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

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

您可以使用adjustingFocus屬性來(lái)確定設(shè)備當(dāng)前是否正在進(jìn)行對(duì)焦。 您可以使用鍵值觀察觀察屬性,以在設(shè)備啟動(dòng)和停止對(duì)焦時(shí)獲得通知。

如果更改對(duì)焦模式設(shè)置,可以按如下方式將其返回到默認(rèn)配置:

if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {

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

    [currentDevice setFocusPointOfInterest:autofocusPoint];

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

有兩種曝光模式:

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

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

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

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

if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {

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

    [currentDevice setExposurePointOfInterest:exposurePoint];

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

有三種閃光模式:

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

手電筒模式

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

您使用hasTorch來(lái)確定設(shè)備是否有閃關(guān)燈。 您使用isTorchModeSupported:方法來(lái)確定設(shè)備是否支持給定的閃關(guān)燈模式,然后使用torchMode屬性設(shè)置模式。

對(duì)于帶手電筒的設(shè)備,手電筒只有在設(shè)備與正在運(yùn)行的捕獲會(huì)話關(guān)聯(lián)時(shí)才會(huì)打開(kāi)。

視頻穩(wěn)定

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

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

白平衡

有兩種白平衡模式:

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

您可以使用adjustingWhiteBalance屬性來(lái)確定設(shè)備當(dāng)前是否正在更改其白平衡設(shè)置。 您可以使用鍵值觀察觀察屬性,以在設(shè)備啟動(dòng)或停止更改其白平衡設(shè)置時(shí)獲得通知。

設(shè)置設(shè)備方向

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

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

List 4-1設(shè)置捕獲連接的方向

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

if ([captureConnection isVideoOrientationSupported]) {

      AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;

     [captureConnection setVideoOrientation:orientation];
}
配置設(shè)備

要在設(shè)備上設(shè)置捕獲屬性,必須先使用lockForConfiguration:獲取設(shè)備的鎖定。 這樣可以避免與其他應(yīng)用程序中的設(shè)置不兼容的更改。 以下代碼片段說(shuō)明了如何通過(guò)首先確定是否支持該模式,然后嘗試鎖定設(shè)備以進(jìn)行重新配置來(lái)改變?cè)O(shè)備上的聚焦模式。 僅當(dāng)獲得鎖定時(shí)才改變對(duì)焦模式,然后立即釋放鎖定。

if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {

    NSError *error = nil;

    if ([device lockForConfiguration:&error]) {

        device.focusMode = AVCaptureFocusModeLocked;

        [device unlockForConfiguration];

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

只有當(dāng)您需要可設(shè)置的設(shè)備屬性保持不變時(shí),才應(yīng)該保持設(shè)備鎖定。 不必要地持有設(shè)備鎖會(huì)降低共享設(shè)備的其他應(yīng)用程序的捕獲效果。

在設(shè)備之間切換

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

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

[session beginConfiguration];

[session removeInput:frontFacingCameraDeviceInput];

[session addInput:backFacingCameraDeviceInput];

[session commitConfiguration];

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

使用捕獲輸入將捕獲設(shè)備添加到會(huì)話

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

NSError *error;

AVCaptureDeviceInput *input =

[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

if (!input) {

// Handle the error appropriately.

}

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

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

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

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

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

使用捕獲輸出從會(huì)話獲取輸出

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

您可以使用addOutput:將輸出添加到捕獲會(huì)話。 您可以使用canAddOutput:檢查捕獲輸出是否與現(xiàn)有會(huì)話兼容。 您可以在會(huì)話運(yùn)行時(shí)根據(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對(duì)象將電影數(shù)據(jù)保存到文件。(AVCaptureMovieFileOutputAVCaptureFileOutput的具體子類,它定義了大部分的基本行為。)您可以配置影片文件輸出的各個(gè)方面,例如錄制的最長(zhǎng)持續(xù)時(shí)間或文件的最大尺寸。 如果剩余的磁盤(pán)空間少于一定數(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#>;

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

開(kāi)始錄制

您可以使用startRecordingToOutputFileURL:recordingDelegate:來(lái)錄制QuickTime影片。您需要提供一個(gè)基于文件的URL和代理。 URL不能標(biāo)識(shí)現(xiàn)有文件,因?yàn)殡娪拔募敵霾粫?huì)覆蓋現(xiàn)有資源。 您還必須具有寫(xiě)入指定位置的權(quán)限。 代理必須符合AVCaptureFileOutputRecordingDelegate協(xié)議,并且必須實(shí)現(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#>];

在實(shí)現(xiàn)captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:時(shí),代理可能會(huì)將生成的電影寫(xiě)入Camera Roll相冊(cè)。 它還應(yīng)該檢查可能發(fā)生的任何錯(cuò)誤。

確保文件成功寫(xiě)入

要確定文件是否成功保存,在實(shí)現(xiàn)captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:時(shí),您不僅要檢查錯(cuò)誤,還檢查錯(cuò)誤的用戶信息字典(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...

您應(yīng)該檢查AVErrorRecordingSuccessfullyFinishedKeykey的值,因?yàn)樵撐募赡芤殉晒Ρ4妫词狗祷亓艘粋€(gè)錯(cuò)誤。 錯(cuò)誤可能表示您的錄制達(dá)到了一個(gè)約束 - 例如,AVErrorMaximumDurationReached或者AVErrorMaximumFileSizeReached。 其他導(dǎo)致錄制結(jié)束的原因是:

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

你也可以隨時(shí)設(shè)置影片文件的元數(shù)據(jù),即使在錄制時(shí)。 對(duì)于在記錄開(kāi)始時(shí)信息不可用的情況(如位置信息一樣)下這是很有用的。 文件輸出的元數(shù)據(jù)由AVMetadataItem對(duì)象的數(shù)組表示; 您可以使用其可變子類AVMutableMetadataItem的實(shí)例來(lái)創(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對(duì)象使用委托來(lái)公開(kāi)視頻幀。您可以使用setSampleBufferDelegate:queue:設(shè)置委托。除了設(shè)置委托,您還可以指定一個(gè)在其上調(diào)用委托方法的串行隊(duì)列。您必須使用串行隊(duì)列來(lái)確保視頻幀以適當(dāng)?shù)捻樞騻鬟f給代理。您可以使用隊(duì)列來(lái)修改傳遞的優(yōu)先級(jí)和處理視頻幀。有關(guān)示例實(shí)現(xiàn),請(qǐng)參閱SquareCam。

這些視頻幀在委托方法captureOutput:didOutputSampleBuffer:fromConnection:中,作為CMSampleBufferRef的實(shí)例(請(qǐng)參閱 Representations of Media)。默認(rèn)情況下,緩沖區(qū)以相機(jī)最有效的格式發(fā)出。您可以使用videoSettings屬性來(lái)指定自定義輸出格式。視頻設(shè)置屬性是一個(gè)字典;目前唯一支持的鍵是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àng)

您應(yīng)該將會(huì)話輸出設(shè)置為應(yīng)用程序的最低實(shí)際分辨率。將輸出設(shè)置為更高的分辨率會(huì)浪費(fèi)處理周期,并且耗電。

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

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

捕捉靜態(tài)圖像

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

像素和編碼格式

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

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

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

捕獲圖像

當(dāng)您要捕獲圖像時(shí),您可以給輸出發(fā)送一個(gè)captureStillImageAsynchronouslyFromConnection:completionHandler:消息。 第一個(gè)參數(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:的第二個(gè)參數(shù)是一個(gè)包含兩個(gè)參數(shù)的block:一個(gè)包含圖像數(shù)據(jù)的CMSampleBuffer和一個(gè)錯(cuò)誤。 樣本緩沖區(qū)本身可以包含諸如EXIF字典的元數(shù)據(jù)作為附件。 您可以根據(jù)需要修改附件,但請(qǐng)注意 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)容

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

視頻預(yù)覽

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

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];

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

視頻重力模式

預(yù)覽圖層支持三種重力模式,可以使用videoGravity設(shè)置:

在預(yù)覽中使用“點(diǎn)擊聚焦”

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

顯示音頻音量

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

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

  • 創(chuàng)建AVCaptureSession對(duì)象以協(xié)調(diào)從AV輸入到輸出的數(shù)據(jù)流。
  • 找到您想要的輸入類型的AVCaptureDevice對(duì)象
  • 為設(shè)備創(chuàng)建AVCaptureDeviceInput對(duì)象
  • 創(chuàng)建AVCaptureVideoDataOutput對(duì)象以產(chǎn)生視頻幀
  • AVCaptureVideoDataOutput對(duì)象實(shí)現(xiàn)代理方法以處理視頻幀
  • 實(shí)現(xiàn)一個(gè)函數(shù)來(lái)將委托接收的CMSampleBuffer轉(zhuǎn)換成UIImage對(duì)象

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

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

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

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

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

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對(duì)象來(lái)處理被捕獲的視頻中的未壓縮幀。 您通常會(huì)配置輸出的幾個(gè)方面。 例如,對(duì)于視頻,您可以使用videoSettings屬性指定像素格式,并通過(guò)設(shè)置minFrameDuration屬性來(lái)限制幀率。

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

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

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

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

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

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

在委托類中,實(shí)現(xiàn)captureOutput:didOutputSampleBuffer:fromConnection:
方法,它會(huì)在樣本緩沖區(qū)被寫(xiě)入時(shí)調(diào)用。 視頻數(shù)據(jù)輸出對(duì)象將幀作為CMSampleBuffer類型傳輸,因此您需要將CMSampleBuffer類型轉(zhuǎn)換為UIImage對(duì)象。 可以參考 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)用代理方法會(huì)在setSampleBufferDelegate:queue:中指定的隊(duì)列上調(diào)用; 如果要更新用戶界面,則必須在主線程上調(diào)用相關(guān)的代碼。

開(kāi)始以及結(jié)束錄制

配置捕獲會(huì)話后,您應(yīng)確保有錄制的權(quán)限。

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];
        });
    }
}];

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

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

[session startRunning];

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

高幀率視頻采集

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

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

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

播放增強(qiáng)了對(duì)慢速和快速播放音頻的支持,從而可以以更慢或更快的速度播放音頻。

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

導(dǎo)出提供兩個(gè)選項(xiàng),支持60 fps視頻。可用的幀率,慢速或快速運(yùn)動(dòng),或者將影片轉(zhuǎn)換為任意較慢的幀速率,例如每秒30幀。

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

回放

AVPlayer的實(shí)例通過(guò)setRate:方法自動(dòng)管理大部分播放速度。該值用作播放速度的乘數(shù)。值為1.0會(huì)導(dǎo)致正常播放,0.5以半速播放,5.0會(huì)使播放比正常快五倍,依此類推。

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

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

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

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

導(dǎo)出

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

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

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

要進(jìn)行自定義錄制,您必須使用AVAssetWriter類,這需要一些額外的設(shè)置。

assetWriterInput.expectsMediaDataInRealTime=YES;

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

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

相關(guān)閱讀更多精彩內(nèi)容

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