iOS 三種錄製視頻方式

UIImagePickerController緩存

目前,將視頻捕獲集成到你的應用中的最簡單的方法是使用 UIImagePickerController。這是一個封裝了完整視頻捕獲管線和相機 UI 的 view controller。session

在實例化相機以前,首先要檢查設備是否支持相機錄製:app

1iphone

2ide

3性能

4優化

5ui

6編碼

7url

8

if ([UIImagePickerController

       isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

    NSArray *availableMediaTypes = [UIImagePickerController

      availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];

    if ([availableMediaTypes containsObject:(NSString *)kUTTypeMovie]) {

        // 支持視頻錄製

    }

}

而後建立一個 UIImagePickerController 對象,設置好代理便於進一步處理錄製好的視頻 (好比存到相冊) 以及對於用戶關閉相機做出響應:

1

2

3

4

UIImagePickerController *camera = [UIImagePickerController new];

camera.sourceType = UIImagePickerControllerSourceTypeCamera;

camera.mediaTypes = @[(NSString *)kUTTypeMovie];

camera.delegate = self;

這是你實現一個功能完善的攝像機所須要寫的全部代碼。

相機配置

UIImagePickerController 提供了額外的配置選項。

經過設置 cameraDevice 屬性能夠選擇一個特定的相機。這是一個 UIImagePickerControllerCameraDevice 枚舉,默認狀況下是 UIImagePickerControllerCameraDeviceRear,你也能夠把它設置爲 UIImagePickerControllerCameraDeviceFront。每次都應事先確認你想要設置的相機是可用的:

1

2

3

4

UIImagePickerController *camera = …

if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {

    [camera setCameraDevice:UIImagePickerControllerCameraDeviceFront];

}

videoQuality 屬性用於控制錄製視頻的質量。它容許你設置一個特定的編碼預設,從而改變視頻的比特率和分辨率。如下是六種預設:

1

2

3

4

5

6

7

8

9

enum {

   UIImagePickerControllerQualityTypeHigh = 0,

   UIImagePickerControllerQualityTypeMedium = 1, // default value

   UIImagePickerControllerQualityTypeLow = 2,

   UIImagePickerControllerQualityType640x480 = 3,

   UIImagePickerControllerQualityTypeIFrame1280x720 = 4,

   UIImagePickerControllerQualityTypeIFrame960x540 = 5

};

typedef NSUInteger  UIImagePickerControllerQualityType;

前三種爲相對預設 (low, medium, high)。這些預設的編碼配置會因設備不一樣而不一樣。若是選擇 high,那麼你選定的相機會提供給你該設備所能支持的最高畫質。後面三種是特定分辨率的預設 (640×480 VGA, 960×540 iFrame, 和 1280×720 iFrame)。

自定義 UI

就像上面提到的,UIImagePickerController 自帶一套相機 UI,能夠直接使用。然而,你也能夠自定義相機的控件,經過隱藏默認控件,而後建立帶有控件的自定義視圖,並覆蓋在相機預覽圖層上面:

1

2

3

UIView *cameraOverlay = …

picker.showsCameraControls = NO;

picker.cameraOverlayView = cameraOverlay;

而後你須要將你覆蓋層上的控件關聯上 UIImagePickerController 的控制方法 (好比,startVideoCapture 和 stopVideoCapture)。

AVFoundation

若是你想要更多關於處理捕獲視頻的方法,而這些方法是 UIImagePickerController 所不能提供的,那麼你須要使用 AVFoundation。

AVFoundation 中關於視頻捕獲的主要的類是 AVCaptureSession。它負責調配影音輸入與輸出之間的數據流:

AVCaptureSession setup

使用一個 capture session,你須要先實例化,添加輸入與輸出,接着啓動從輸入到輸出之間的數據流:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

AVCaptureSession *captureSession = [AVCaptureSession new];

AVCaptureDeviceInput *cameraDeviceInput = …

AVCaptureDeviceInput *micDeviceInput = …

AVCaptureMovieFileOutput *movieFileOutput = …

if ([captureSession canAddInput:cameraDeviceInput]) {

    [captureSession addInput:cameraDeviceInput];

}

if ([captureSession canAddInput:micDeviceInput]) {

    [captureSession addInput:micDeviceInput];

}

if ([captureSession canAddOutput:movieFileOutput]) {

    [captureSession addOutput:movieFileOutput];

}

[captureSession startRunning];

(爲了簡單起見,調度隊列 (dispatch queue) 的相關代碼已經從上面那段代碼中省略了。全部對 capture session 的調用都是阻塞的,所以建議將它們分配到後臺串行隊列中。)

capture session 能夠經過一個 sessionPreset

來進一步配置,這能夠用來指定輸出質量的等級。有 11 種不一樣的預設模式:

1

2

3

4

5

6

7

8

9

10

11

NSString *const  AVCaptureSessionPresetPhoto;

NSString *const  AVCaptureSessionPresetHigh;

NSString *const  AVCaptureSessionPresetMedium;

NSString *const  AVCaptureSessionPresetLow;

NSString *const  AVCaptureSessionPreset352x288;

NSString *const  AVCaptureSessionPreset640x480;

NSString *const  AVCaptureSessionPreset1280x720;

NSString *const  AVCaptureSessionPreset1920x1080;

NSString *const  AVCaptureSessionPresetiFrame960x540;

NSString *const  AVCaptureSessionPresetiFrame1280x720;

NSString *const  AVCaptureSessionPresetInputPriority;

第一個表明高像素圖片輸出。 接下來的九個和以前咱們在設置 UIImagePickerController 的 videoQuality 時看到過的 UIImagePickerControllerQualityType 選項很是類似,不一樣的是,這裏有一些額外可用於 capture session 的預設。 最後一個 (AVCaptureSessionPresetInputPriority) 表明 capture session 不去控制音頻與視頻輸出設置。而是經過已鏈接的捕獲設備的 activeFormat 來反過來控制 capture session 的輸出質量等級。在下一節,咱們將會看到更多關於設備和設備格式的細節。

  • 輸入

AVCaptureSession 的輸入其實就是一個或多個的 AVCaptureDevice 對象,這些對象經過 AVCaptureDeviceInput 鏈接上 capture session。

咱們可使用 [AVCaptureDevice devices] 來尋找可用的捕獲設備。以 iPhone 6 爲例:

1

2

3

4

5

(

    「<avcapturefigvideodevice: 0x136514db0 [back camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>」,

    「<avcapturefigvideodevice: 0x13660be80 [front camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]>」,

    「<avcapturefigaudiodevice: 0x174265e80 [iphone microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]>」

)</avcapturefigaudiodevice: 0x174265e80 [iphone microphone][com.apple.avfoundation.avcapturedevice.built-in_audio:0]></avcapturefigvideodevice: 0x13660be80 [front camera][com.apple.avfoundation.avcapturedevice.built-in_video:1]></avcapturefigvideodevice: 0x136514db0 [back camera][com.apple.avfoundation.avcapturedevice.built-in_video:0]>

  • 視頻輸入

配置相機輸入,須要實例化一個 AVCaptureDeviceInput 對象,參數是你指望的相機設備,而後把它添加到 capture session:

1

2

3

4

5

6

7

AVCaptureSession *captureSession = …

AVCaptureDevice *cameraDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

NSError *error;

AVCaptureDeviceInput *cameraDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:cameraDevice error:&error];

if ([captureSession canAddInput:input]) {

    [captureSession addInput:cameraDeviceInput];

}

若是上面提到的 capture session 預設列表裏能知足你的需求,那你就不須要作更多的事情了。若是不夠,好比你想要高的幀率,你將須要配置具體的設備格式。一個視頻捕獲設備有許多設備格式,每一個都帶有特定的屬性和功能。下面是對於 iPhone6 的後置攝像頭的一些例子 (一共有 22 種可用格式):

baioge600.jpg

  • 格式 = 像素格式

  • FPS = 支持幀數範圍

  • HRSI = 高像素靜態圖片尺寸

  • FOV = 視角

  • VIS = 該格式支持視頻防抖

  • Upscales = 加入數字 upscaling 時的放大比例

  • AF = 自動對焦系統(1 是反差對焦,2 是相位對焦)

  • ISO = 支持感光度範圍

  • SS = 支持曝光時間範圍

  • HDR = 支持高動態範圍圖像

經過上面的那些格式,你會發現若是要錄製 240 幀每秒的視頻的話,能夠根據想要的像素格式選用第一個或第二個格式。另外如果要捕獲 1920×1080 的分辨率的視頻的話,是不支持 240 幀每秒的。

配置一個具體設備格式,你首先須要調用 lockForConfiguration: 來獲取設備的配置屬性的獨佔訪問權限。接着你簡單地使用 setActiveFormat: 來設置設備的捕獲格式。這將會自動把 capture session 的預設設置爲 AVCaptureSessionPresetInputPriority。

一旦你設置了預想的設備格式,你就能夠在這種設備格式的約束參數範圍內進行進一步的配置了。

對於視頻捕獲的對焦,曝光和白平衡的設置,與圖像捕獲時同樣,具體可參考第 21 期「iOS 上的相機捕捉」。除了那些,這裏還有一些視頻特有的配置選項。

你能夠用捕獲設備的 activeVideoMinFrameDuration 和 activeVideoMaxFrameDuration 屬性設置幀速率,一幀的時長是幀速率的倒數。設置幀速率以前,要先確認它是否在設備格式所支持的範圍內,而後鎖住捕獲設備來進行配置。爲了確保幀速率恆定,能夠將最小與最大的幀時長設置成同樣的值:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

NSError *error;

CMTime frameDuration = CMTimeMake(1, 60);

NSArray *supportedFrameRateRanges = [device.activeFormat videoSupportedFrameRateRanges];

BOOL frameRateSupported = NO;

for (AVFrameRateRange *range in supportedFrameRateRanges) {

    if (CMTIME_COMPARE_INLINE(frameDuration, >=, range.minFrameDuration) &&

        CMTIME_COMPARE_INLINE(frameDuration, <=, range.maxFrameDuration)) {

        frameRateSupported = YES;

    }

}

if (frameRateSupported && [device lockForConfiguration:&error]) {

    [device setActiveVideoMaxFrameDuration:frameDuration];

    [device setActiveVideoMinFrameDuration:frameDuration];

    [device unlockForConfiguration];

}

視頻防抖 是在 iOS 6 和 iPhone 4S 發佈時引入的功能。到了 iPhone 6,增長了更強勁和流暢的防抖模式,被稱爲影院級的視頻防抖動。相關的 API 也有所改動 (目前爲止並無在文檔中反映出來,不過能夠查看頭文件)。防抖並非在捕獲設備上配置的,而是在 AVCaptureConnection 上設置。因爲不是全部的設備格式都支持所有的防抖模式,因此在實際應用中應事先確認具體的防抖模式是否支持:

1

2

3

4

5

6

AVCaptureDevice *device = ...;

AVCaptureConnection *connection = ...;

AVCaptureVideoStabilizationMode stabilizationMode = AVCaptureVideoStabilizationModeCinematic;

if ([device.activeFormat isVideoStabilizationModeSupported:stabilizationMode]) {

    [connection setPreferredVideoStabilizationMode:stabilizationMode];

}

iPhone 6 的另外一個新特性就是視頻 HDR (高動態範圍圖像),它是「高動態範圍的視頻流,與傳統的將不一樣曝光度的靜態圖像合成成一張高動態範圍圖像的方法徹底不一樣」,它是內建在傳感器中的。有兩種方法能夠配置視頻 HDR:直接將 capture device 的 videoHDREnabled 設置爲啓用或禁用,或者使用 automaticallyAdjustsVideoHDREnabled 屬性來留給系統處理。

技術參考:iPhone 6 和 iPhone Plus 的新 AV Foundation 相機特性

  • 音頻輸入

以前展現的捕獲設備列表裏面只有一個音頻設備,你可能以爲奇怪,畢竟 iPhone 6 有 3 個麥克風。然而由於有時會放在一塊兒使用,便於優化性能,所以可能被當作一個設備來使用。例如在 iPhone 5 及以上的手機錄製視頻時,會同時使用前置和後置麥克風,用於定向降噪。

Technical Q&A: AVAudioSession – Microphone Selection

大多數狀況下,設置成默認的麥克風配置便可。後置麥克風會自動搭配後置攝像頭使用 (前置麥克風則用於降噪),前置麥克風和前置攝像頭也是同樣。

然而想要訪問和配置單獨的麥克風也是可行的。例如,當用戶正在使用後置攝像頭捕獲場景的時候,使用前置麥克風來錄製解說也應是可能的。這就要依賴於 AVAudioSession。 爲了變動要訪問的音頻,audio session 首先須要設置爲支持這樣作的類別。而後咱們須要遍歷 audio session 的輸入端口和端口數據來源,來找到咱們想要的麥克風:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// 配置 audio session

AVAudioSession *audioSession = [AVAudioSession sharedInstance];

[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];

[audioSession setActive:YES error:nil];

// 尋找指望的輸入端口

NSArray* inputs = [audioSession availableInputs];

AVAudioSessionPortDescription *builtInMic = nil;

for (AVAudioSessionPortDescription* port in inputs) {

    if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {

        builtInMic = port;

        break;

    }

}

// 尋找指望的麥克風

for (AVAudioSessionDataSourceDescription* source in builtInMic.dataSources) {

    if ([source.orientation isEqual:AVAudioSessionOrientationFront]) {

        [builtInMic setPreferredDataSource:source error:nil];

        [audioSession setPreferredInput:builtInMic error:&error];

        break;

    }

}

除了設置非默認的麥克風配置,你也可使用 AVAudioSession 來配置其餘音頻設置,好比音頻增益和採樣率等。

  • 訪問權限

有件事你須要記住,訪問相機和麥克風須要先得到用戶受權。當你給視頻或音頻建立第一個 AVCaptureDeviceInput 對象時,iOS 會自動彈出一次對話框,請求用戶受權,但你最好仍是本身實現下。以後你就能夠在尚未被受權的時候,使用相同的代碼來提示用戶進行受權。當用戶未受權時,對於錄製視頻或音頻的嘗試,獲得的將是黑色畫面和無聲。

  • 輸出

輸入配置完了,如今把咱們的注意力轉向 capture session 的輸出。

AVCaptureMovieFileOutput

將視頻寫入文件,最簡單的選擇就是使用 AVCaptureMovieFileOutput 對象。把它做爲輸出添加到 capture session 中,就能夠將視頻和音頻寫入 QuickTime 文件,這隻需不多的配置。

1

2

3

4

5

6

7

AVCaptureMovieFileOutput *movieFileOutput = [AVCaptureMovieFileOutput new];

if([captureSession canAddOutput:movieFileOutput]){

    [captureSession addOutput:movieFileOutput];

}

// 開始錄製

NSURL *outputURL = …

[movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];

當實際的錄製開始或中止時,想要接收回調的話就必需要一個錄製代理。當錄製中止時,輸出一般還在寫入數據,等它完成以後會調用代理方法。

AVCaptureMovieFileOutput 有一些其餘的配置選項,好比在某段時間後,在達到某個指定的文件尺寸時,或者當設備的最小磁盤剩餘空間達到某個閾值時中止錄製。若是你還須要更多設置,好比自定義視頻音頻的壓縮率,或者你想要在寫入文件以前,處理視頻音頻的樣本,那麼你須要一些更復雜的操做。

AVCaptureDataOutput 和 AVAssetWriter

若是你想要對影音輸出有更多的操做,你可使用 AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 而不是咱們上節討論的 AVCaptureMovieFileOutput。

這些輸出將會各自捕獲視頻和音頻的樣本緩存,接着發送到它們的代理。代理要麼對採樣緩衝進行處理 (好比給視頻加濾鏡),要麼保持原樣傳送。使用 AVAssetWriter 對象能夠將樣本緩存寫入文件:

Using an AVAssetWriter

配置一個 asset writer 須要定義一個輸出 URL 和文件格式,並添加一個或多個輸入來接收採樣的緩衝。咱們還須要將輸入的 expectsMediaInRealTime屬性設置爲 YES,由於它們須要從 capture session 實時得到數據。

1

2

3

4

5

6

7

8

9

10

11

12

NSURL *url = …;

AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:url fileType:AVFileTypeMPEG4 error:nil];

AVAssetWriterInput *videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:nil];

videoInput.expectsMediaDataInRealTime = YES;

AVAssetWriterInput *audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:nil];

audioInput.expectsMediaDataInRealTime = YES;

if ([assetWriter canAddInput:videoInput]) {

    [assetWriter addInput:videoInput];

}

if ([assetWriter canAddInput:audioInput]) {

    [assetWriter addInput:audioInput];

}

(這裏推薦將 asset writer 派送到後臺串行隊列中調用。)

在上面的示例代碼中,咱們將 asset writer 的 outputSettings 設置爲 nil。這就意味着附加上來的樣本不會再被從新編碼。若是你確實想要從新編碼這些樣本,那麼須要提供一個包含具體輸出參數的字典。關於音頻輸出設置的鍵值被定義在這裏, 關於視頻輸出設置的鍵值定義在這裏。

爲了更簡單點,AVCaptureVideoDataOutput 和 AVCaptureAudioDataOutput 分別帶有 recommendedVideoSettingsForAssetWriterWithOutputFileType: 和 recommendedAudioSettingsForAssetWriterWithOutputFileType: 方法,能夠生成與 asset writer 兼容的帶有所有鍵值對的字典。因此你能夠經過在這個字典裏調整你想要重寫的屬性,來簡單地定義你本身的輸出設置。好比,增長視頻比特率來提升視頻質量等。

或者,你也可使用 AVOutputSettingsAssistant 來配置輸出設置的字典,可是從個人經驗來看,使用上面的方法會更好,它們會提供更實用的輸出設置,好比視頻比特率。另外,AVOutputSettingsAssistant 彷佛存在一些缺點,例如,當你改變但願的視頻的幀速率時,視頻的比特率並不會改變。

實時預覽

當使用 AVFoundation 來作圖像捕獲時,咱們必須提供一套自定義的用戶界面。其中一個關鍵的相機交互組件是實時預覽圖。最簡單的實現方式是經過把 AVCaptureVideoPreviewLayer 對象做爲一個 sublayer 加到相機圖層上去:

1

2

3

4

5

AVCaptureSession *captureSession = ...;

AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];

UIView *cameraView = ...;

previewLayer.frame = cameraView.bounds;

[cameraView.layer addSublayer:previewLayer];

若是你想要更進一步操做,好比,在實時預覽圖加濾鏡,你須要將 AVCaptureVideoDataOutput 對象加到 capture session,而且使用 OpenGL 展現畫面,具體可查看該文「iOS 上的相機捕捉」

總結

有許多不一樣的方法能夠給 iOS 上的視頻捕獲配置管線,從最直接的 UIImagePickerController,到精密配合的 AVCaptureSession 與 AVAssetWriter。如何抉擇取決於你的項目要求,好比指望的視頻質量和壓縮率,或者是你想要展現給用戶的相機控件。

相關文章
相關標籤/搜索