iOS視訊流採集概述(AVCaptureSession)

小東邪發表於2019-04-13

需求:需要採集到視訊幀資料從而可以進行一系列處理(如: 裁剪,旋轉,美顏,特效....). 所以,必須採集到視訊幀資料.


閱讀前提:

  • 使用AVFoundation框架
  • 採集音視訊幀資料

GitHub地址(附程式碼) : iOS視訊流採集概述

簡書地址 : iOS視訊流採集概述

部落格地址 : iOS視訊流採集概述

掘金地址 : iOS視訊流採集概述


注意:本文僅僅是原理性講解,而實際相機的設定也是比較複雜,具體相機引數的設定請參考另一篇 - iOS相機設定實戰


Overview

AVCaptureSession:使用相機或麥克風實時採集音視訊資料流.

  • AVCaptureSession : 管理輸入輸出音視訊流

  • AVCaptureDevice : 相機硬體的介面,用於控制硬體特性,諸如鏡頭的位置(前後攝像頭)、曝光、閃光燈等。

  • AVCaptureInput : 配置輸入裝置,提供來自裝置的資料

  • AVCaptureOutput : 管理輸出的結果(音視訊資料流)

  • AVCaptureConnection: 表示輸入與輸出的連線

  • AVCaptureVideoPreviewLayer: 顯示當前相機正在採集的狀況

一個session可以配置多個輸入輸出

1.AVCaptureSession

下圖展示了向session中新增輸入輸出後的連線情況

2.AVCaptureConnection

授權

首先需要在Info.plist檔案中新增鍵Privacy - Camera Usage Description以請求相機許可權.

注意: 如果不新增,程式crash,如果使用者不給許可權,則會顯示全黑的相機畫面.

1. 使用Capture Session管理資料流

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

複製程式碼

1.1. 使用preset配置解析度,幀率

  • canSetSessionPreset:檢查是否支援指定解析度
  • setActiveVideoMinFrameDuration: 設定幀率最小值
  • setActiveVideoMaxFrameDuration: 設定幀率最大值

CMTimeMake: 分子為1,即每秒鐘來多少幀.

  • 在低幀率下(幀率<=30)可以用如下方式設定
- (void)setCameraResolutionByPresetWithHeight:(int)height session:(AVCaptureSession *)session {
    [session beginConfiguration];
    session.sessionPreset = preset;
    [session commitConfiguration];
}

- (void)setCameraForLFRWithFrameRate:(int)frameRate {
    // Only for frame rate <= 30
    AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    [captureDevice lockForConfiguration:NULL];
    [captureDevice setActiveVideoMinFrameDuration:CMTimeMake(1, frameRate)];
    [captureDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, frameRate)];
    [captureDevice unlockForConfiguration];
}
複製程式碼
  • 高幀率下設定解析度(幀率>30)

如果需要對某一解析度支援高幀率的設定,如50幀,60幀,120幀...,原先setActiveVideoMinFrameDurationsetActiveVideoMaxFrameDuration是無法做到的,Apple規定我們需要使用新的方法設定幀率setActiveVideoMinFrameDurationsetActiveVideoMaxFrameDuration,並且該方法必須配合新的設定解析度activeFormat的方法一起使用.

新的設定解析度的方法activeFormatsessionPreset是互斥的,如果使用了一個, 另一個會失效,建議直接使用高幀率的設定方法,廢棄低幀率下設定方法,避免產生相容問題。

Apple在更新方法後將原先分離的解析度與幀率的設定方法合二為一,原先是單獨設定相機解析度與幀率,而現在則需要一起設定,即每個解析度有其對應支援的幀率範圍,每個幀率也有其支援的解析度,需要我們遍歷來查詢,所以原先統一的單獨的設定解析度與幀率的方法在高幀率模式下相當於棄用,可以根據專案需求選擇,如果確定專案不會支援高幀率(fps>30),可以使用以前的方法,簡單且有效.

注意: 使用activeFormat方法後,之前使用sessionPreset方法設定的解析度將自動變為AVCaptureSessionPresetInputPriority,所以如果專案之前有用canSetSessionPreset比較的if語句也都將失效,建議如果專案必須支援高幀率則徹底啟用sessionPreset方法.

具體設定方法參考另一篇文章:iOS相機設定實戰

注意: 在將session配置為使用用於高解析度靜態拍攝的活動格式並將以下一個或多個操作應用於AVCaptureVideoDataOutput時,系統可能無法滿足目標幀速率:縮放,方向更改,格式轉換。

1.2. 更改相機設定

如果你需要在開啟相機後進一步調節相機引數,在beginConfigurationcommitConfiguration中寫入更改的程式碼.呼叫beginConfiguration後可以新增移除輸入輸出,更改解析度,配置個別的輸入輸出屬性,直到呼叫commitConfiguration所有的更改才會生效.

[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];



複製程式碼

1.3. 監聽Session狀態

可以使用通知監聽相機當前狀態,如開始,停止,意外中斷等等...

  • 監聽掉幀
- (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
複製程式碼
  • 處理相機執行中突然出錯
[kTVUNotification addObserver:self selector:@selector(handleCameraRuntimeError)
                         name:AVCaptureSessionRuntimeErrorNotification
                       object:nil];
[kTVUNotification addObserver:self selector:@selector(handleCameraInterruptionEndedError)
                         name:AVCaptureSessionInterruptionEndedNotification
                       object:nil];
[kTVUNotification addObserver:self selector:@selector(handleCameraWasInterruptedError)
                         name:AVCaptureSessionWasInterruptedNotification
                       object:nil];
複製程式碼

2. AVCaptureDevice表示輸入裝置

2.1. 定義

AVCaptureDevice物件是關於相機硬體的介面,用於控制硬體特性,諸如鏡頭的位置、曝光、閃光燈等。

2.2. 獲取裝置

使用AVCaptureDevice的devicesdevicesWithMediaType:方法可以找到我們需要的裝置, 可用裝置列表可能會發生變化, 如它們被別的應用使用,或一個新的輸入裝置接入(如耳機),通過註冊AVCaptureDeviceWasConnectedNotification,AVCaptureDeviceWasDisconnectedNotification可以在裝置變化時得到通知.

2.3. 裝置特性

可以通過程式碼獲取當前輸入裝置的位置(前後置攝像頭)以及其他硬體相關資訊.

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");
        }
    }
}
複製程式碼

2.4. 相機功能設定

不同裝置具有不同的功能,如果需要可以開啟對應的功能

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSMutableArray *torchDevices = [[NSMutableArray alloc] init];
 
for (AVCaptureDevice *device in devices) {
    [if ([device hasTorch] &&
         [device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
        [torchDevices addObject:device];
    }
}
複製程式碼

注意:在設定相機屬性前,總是先通過API查詢當前裝置是否支援該功能,再進行相應處理

  • 聚焦模式-Focus Modes
    • AVCaptureFocusModeLocked: 設定一個固定的聚焦點
    • AVCaptureFocusModeAutoFocus: 首次自動對焦然後鎖定一個聚焦點
    • AVCaptureFocusModeContinuousAutoFocus: 指當場景改變,相機會自動重新對焦到畫面的中心點
isFocusModeSupported: 查詢裝置是否支援.
adjustingFocus: 判斷一個裝置是否正在改變對焦點
複製程式碼

使用focusPointOfInterestSupported測試裝置是否支援設定對焦點,如果支援,使用focusPointOfInterest設定聚焦點,{0,0}代表畫面左上角座標,{1,1}代表右下角座標.

if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
    CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f);
    [currentDevice setFocusPointOfInterest:autofocusPoint];
    [currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}

複製程式碼
  • 曝光模式-Exposure Modes
    • AVCaptureExposureModeContinuousAutoExposure: 自動調節曝光模式
    • AVCaptureExposureModeLocked: 固定的曝光模式
isExposureModeSupported:是否支援某個曝光模式
adjustingExposure:判斷一個裝置是否正在改變曝光值
複製程式碼
if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
    CGPoint exposurePoint = CGPointMake(0.5f, 0.5f);
    [currentDevice setExposurePointOfInterest:exposurePoint];
    [currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}

複製程式碼
  • 閃光燈模式-Flash Modes
    • AVCaptureFlashModeOff: 永不開啟
    • AVCaptureFlashModeOn: 總是開啟
    • AVCaptureFlashModeAuto: 自動開啟,根據光線判斷
hasFlash:是否有閃光燈
isFlashModeSupported:是否支援閃光燈模式
複製程式碼
  • 手電筒模式-Torch Mode
    • AVCaptureTorchModeOff
    • AVCaptureTorchModeOn
    • AVCaptureTorchModeAuto
hasTorch: 是否有手電筒
isTorchModeSupported: 是否支援手電筒模式
複製程式碼

手電筒只有在相機開啟時才能開啟

  • 視訊穩定性-Video Stabilization
    • videoStabilizationEnabled
    • enablesVideoStabilizationWhenAvailable

該功能預設是關閉的,畫面穩定功能依賴於裝置特定的硬體,並且不是所有格式的後設資料與解析度都支援此功能.

開啟該功能可能造成畫面延遲

  • 白平衡-White Balance
    • AVCaptureWhiteBalanceModeLocked
    • AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance
isWhiteBalanceModeSupported: 是否支援白平衡模式
adjustingWhiteBalance: 是否正在調整白平衡
複製程式碼

相機為了適應不同型別的光照條件需要補償。這意味著在冷光線的條件下,感測器應該增強紅色部分,而在暖光線下增強藍色部分。在 iPhone 相機中,裝置會自動決定合適的補光,但有時也會被場景的顏色所混淆失效。幸運地是,iOS 8 可以裡手動控制白平衡。

自動模式工作方式和對焦、曝光的方式一樣,但是沒有“感興趣的點”,整張影像都會被納入考慮範圍。在手動模式,我們可以通過開爾文所表示的溫度來調節色溫和色彩。典型的色溫值在 2000-3000K (類似蠟燭或燈泡的暖光源) 到 8000K (純淨的藍色天空) 之間。色彩範圍從最小的 -150 (偏綠) 到 150 (偏品紅)。

  • 設定裝置方向
    • AVCaptureConnectionsupportsVideoOrientation:
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.
複製程式碼
  • 切換裝置
AVCaptureSession *session = <#A capture session#>;
[session beginConfiguration];
 
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
 
[session commitConfiguration];
複製程式碼

3. 配置Capture Inputs新增到Session中

一個AVCaptureInput代表一種或多種媒體資料,比如,輸入裝置可以同時提供視訊和音訊資料.每種媒體流代表一個AVCaptureInputPort物件.使用AVCaptureConnection可以將AVCaptureInputPort與AVCaptureOutput連線起來.

NSError *error;
AVCaptureDeviceInput *input =
        [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
    // Handle the error appropriately.
}

AVCaptureSession *captureSession = <#Get a capture session#>;
AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>;
if ([captureSession canAddInput:captureDeviceInput]) {
    [captureSession addInput:captureDeviceInput];
}
else {
    // Handle the failure.
}


複製程式碼

4. 使用Capture Outputs從Session中獲取輸出流

AVCaptureOutput: 從session中獲取輸出流.

  • AVCaptureMovieFileOutput: 將資料寫入檔案
  • AVCaptureVideoDataOutput: 將視訊資料以回撥形式輸出視訊幀
  • AVCaptureAudioDataOutput: 將音訊資料以回撥形式輸出音訊幀
  • AVCaptureStillImageOutput: 捕捉靜態圖片
addOutput: 新增輸出
canAddOutput: 是否能新增

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.
}

複製程式碼

4.1. AVCaptureMovieFileOutput

4.1.1. 寫入檔案

AVCaptureMovieFileOutput: 使用此類作為輸出.可以配置錄製最長時間,檔案大小以及禁止在磁碟空間不足時繼續錄製等等.

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#>;
複製程式碼
4.1.2. 開始錄製

你需要提供一個檔案儲存地址的URL以及代理去監聽狀態,這個代理是AVCaptureFileOutputRecordingDelegate, 必須實現captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:代理方法

URL不能是已經存在的檔案,因為無法重寫.

AVCaptureMovieFileOutput *aMovieFileOutput = <#Get a movie file output#>;
NSURL *fileURL = <#A file URL that identifies the output location#>;
[aMovieFileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:<#The delegate#>];
複製程式碼
4.1.3. 確保檔案寫入成功

通過代理方法可以檢查是否寫入成功

需要檢查AVErrorRecordingSuccessfullyFinishedKey的值,因為可能寫入沒有錯誤,但由於磁碟記憶體不足導致最終寫入失敗.

寫入失敗的原因

  • 磁碟記憶體不足(AVErrorDiskFull)
  • 錄製裝置失去連線(AVErrorDeviceWasDisconnected)
  • session意外中斷(AVErrorSessionWasInterrupted)
- (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...
複製程式碼
4.1.4. 新增Metadata到檔案

可以在任意時間設定輸出檔案的metadata資訊,即使正在錄製.

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;

複製程式碼

4.2 AVCaptureVideoDataOutput

4.2.1. 獲取視訊幀資料

AVCaptureVideoDataOutput物件可以通過代理(setSampleBufferDelegate:queue:)獲取實時的視訊幀資料.同時需要指定一個接受視訊幀的序列佇列.

必須使用序列佇列,因為要保證視訊幀是按順序傳輸給代理方法

captureOutput:didOutputSampleBuffer:fromConnection:代理方法中接受視訊幀,每個視訊幀被存放在CMSampleBufferRef引用物件中, 預設這些buffers以相機最有效的格式發出,我們也可以通過videoSettings指定輸出相機的格式.需要將要指定的格式設定為kCVPixelBufferPixelFormatTypeKey的value,使用availableVideoCodecTypes可以查詢當前支援的相機格式.

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

複製程式碼
4.3. AVCaptureStillImageOutput

如果要使用附帶metadata後設資料的靜止影像,需要使用AVCaptureStillImageOutput.

  • 畫素與編碼格式

使用availableImageDataCVPixelFormatTypes, availableImageDataCodecTypes獲取當前支援的格式,以便於查詢是否支援你想要設定的格式.

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = @{ AVVideoCodecKey : AVVideoCodecJPEG};
[stillImageOutput setOutputSettings:outputSettings];
複製程式碼
  • 採集影像

向output傳送一條captureStillImageAsynchronouslyFromConnection:completionHandler:訊息以採集一張影像.

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

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

複製程式碼

5. 展示預覽圖

如果相機的session已經開始工作,我們可以為使用者建立一個預覽圖展示當前相機採集的狀況(即就像系統相機拍攝視訊時的預覽介面)

5.1. Video Preview

  • AVCaptureVideoPreviewLayer: 展示相機預覽情況,CALayer的子類.
  • 使用AVCaptureVideoDataOutput可以將畫素層呈現給使用者

a video preview layer保持對它關聯session的強引用,為了確保在圖層嘗試顯示視訊時不會被釋放

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];
複製程式碼

preview layer是CALayer的子類,因此它具有CALayer的行為,你可以對這一圖層執行轉換,旋轉等操作.

5.1.1. 視訊重力感應模式
  • AVLayerVideoGravityResizeAspect: 保持解析度的原始尺寸,即橫縱比,未填充的螢幕區域會有黑條
  • AVLayerVideoGravityResizeAspectFill: 保持橫縱比,鋪滿螢幕時可以犧牲部分畫素
  • AVLayerVideoGravityResize: 拉伸視訊以充滿螢幕,影像會失真
5.1.2. 點選聚焦

實現帶有預覽層的對焦時,必須考慮預覽層的預覽方向和重力以及映象預覽的可能性.

5.2. 顯示Audio Levels

注意:一般採集音訊不使用AVCaptureSession, 而是用更底層的AudioQueue, AudioUnit, 如需幫助請參考另一篇文章: 音訊採集

使用AVCaptureAudioChannel物件監視捕獲連線中音訊通道的平均功率和峰值功率級別.音訊級不支援KVO,因此必須經常輪詢更新級別,以便更新使用者介面(例如,每秒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.
    }
}

複製程式碼

6. 總結

下面將介紹如何採集視訊幀並將其轉換為UIImage物件.

6.1. 流程

  • 建立AVCaptureSession物件管理輸入輸出流
  • 建立AVCaptureDevice物件管理當前硬體支援的所有裝置,可以遍歷找到我們需要的裝置
  • 建立AVCaptureDeviceInput物件表示具體的的輸入端的硬體裝置
  • 建立AVCaptureVideoDataOutput物件管理輸出視訊幀
  • 實現AVCaptureVideoDataOutput代理方法以產生視訊幀
  • 將視訊幀從CMSampleBuffer格式轉為UIImage格式

下面是簡單流程實現

  • 建立並配置session物件
AVCaptureSession *session = [[AVCaptureSession alloc] init];
session.sessionPreset = AVCaptureSessionPresetMedium;

複製程式碼
  • 建立並配置裝置的輸入端
AVCaptureDevice *device =
        [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
 
NSError *error = nil;
AVCaptureDeviceInput *input =
        [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
    // Handle the error appropriately.
}
[session addInput:input];

複製程式碼
  • 建立並配置輸出端

通過配置AVCaptureVideoDataOutput物件(如視訊幀的格式, 幀率),以產生未壓縮的原始資料.

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

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

複製程式碼
  • 實現代理方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput
         didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
         fromConnection:(AVCaptureConnection *)connection {
 
    UIImage *image = imageFromSampleBuffer(sampleBuffer);
    // Add your code here that uses the image.
}
複製程式碼
  • 開始/停止錄製

配置完capture session之後,確保應用程式擁有許可權.

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

[session startRunning];
[session stopRunning];
複製程式碼

注意: startRunning是一個同步的方法,它可能會花一些時間,因此可能阻塞執行緒(可以在同步佇列中執行避免主執行緒阻塞).

7. 補充

iOS7.0 介紹了高幀率視訊採集,我們需要使用AVCaptureDeviceFormat類,該類具有返回支援的影像型別,幀率,縮放比例,是否支援穩定性等等.

  • 支援720p, 60幀,同時保證視訊穩定性
  • 相容音訊的倍速播放
  • 編輯支援可變組合中的縮放編輯 (Editing has full support for scaled edits in mutable compositions.)
  • 匯出可以支援可變幀率的60fps或者將其轉為較低幀率如30fps

7.1. 播放

AVPlayer的一個例項通過設定setRate:方法值自動管理大部分播放速度。該值用作播放速度的乘數。值為1.0會導致正常播放,0.5以半速播放,5.0播放比正常播放快5倍,依此類推。

AVPlayerItem物件支援audioTimePitchAlgorithm屬性。此屬性允許您指定在使用“時間間距演算法設定”常量以各種幀速率播放影片時播放音訊的方式。

7.2. 編輯

使用AVMutableComposition物件完成編輯操作

7.3. 匯出

使用AVAssetExportSession匯出60fps的視訊檔案

  • AVAssetExportPresetPassthrough: 避免重新編碼視訊。它將媒體的部分標記為部分60 fps,部分減速或部分加速.
  • frameDuration: 使用恆定幀速率匯出以獲得最大的播放相容性,可以使用audioTimePitchAlgorithm指定時間.

7.4. 錄製

使用AVCaptureMovieFileOutput自動支援高幀率的錄製,它將自動選擇正確的H264的音高與位元率.如果需要對錄製做一些額外操作,需要用到AVAssetWriter.

assetWriterInput.expectsMediaDataInRealTime=YES;
複製程式碼

相關文章