本文轉自:AVAudioFoundation(4):音視頻錄製 | www.samirchen.comhtml
本文主要內容來自 AVFoundation Programming Guide。ios
採集設備的音視頻時,咱們須要組裝各路數據,這時可使用 AVCaptureSession
對象來協調。git
AVCaptureDevice
對象表示輸入設備,好比攝像頭或者麥克風。AVCaptureInput
具體子類的實例能夠用來配置輸出設備的端口。AVCaptureOutput
具體子類的實例能夠用來將音視頻數據輸出到一個視頻文件或靜態圖片。AVCaptureSession
實例用來協調輸入輸出的數據流。在錄製視頻時,爲了讓用戶看到預覽效果,咱們可使用 AVCaptureVideoPreviewLayer
。session
下圖展現了經過一個 capture session 實例來協調多路輸入輸出數據:app
對於大多數應用場景,這些細節已經足夠咱們用了。可是對於有些操做,好比當咱們想要監測一個音頻通道的強度,咱們須要瞭解不一樣的輸入設備的端口對應的對象,以及這些端口和輸出是如何鏈接起來的。async
在音視頻錄製時,輸入和輸出之間的鏈接是用 AVCaptureConnection
來表示的。輸入方(AVCaptureInput
)包含一個或多個輸入端口(AVCaptureInputPort
),輸出端(AVCaptureOutput
)能夠從一個或多個數據源接收數據,好比一個 AVCaptureMovieFileOutput
就能夠同時接收視頻和音頻數據。ide
當你往一次錄製 session 中添加一個輸入或輸出時,這個 session 會生成全部兼容的輸入和輸出端口間的鏈接,由 AVCaptureConnection
對象表示。性能
You can use a capture connection to enable or disable the flow of data from a given input or to a given output. You can also use a connection to monitor the average and peak power levels in an audio channel.測試
咱們能夠用 connetion 對象來控制輸入輸出端之間的數據流的斷開或鏈接,咱們還能用它來監控 audio 通道的平均值和峯值。優化
AVCaptureSession
是咱們用來管理數據捕獲的核心協調者,咱們用它來協調音視頻輸入和輸出端的數據流。咱們能夠將咱們須要的捕獲設備添加到 session 中,而後用 startRunning
接口啓動數據流,用 stopRunning
中止數據流。
AVCaptureSession *session = [[AVCaptureSession alloc] init]; // Add inputs and outputs. [session startRunning];
咱們能夠給 session 設置咱們須要的圖像質量和分辨率。如下是其中的幾個配置選項:
若是要使用某種分辨率選項,咱們須要先測試一下設備是否支持:
if ([session canSetSessionPreset:AVCaptureSessionPreset1280x720]) { session.sessionPreset = AVCaptureSessionPreset1280x720; } else { // Handle the failure. }
若是想對 session 的配置參數作更細粒度的控制,或者想修改已經在運行狀態的 session 的配置參數,咱們須要在 beginConfiguration
和 commitConfiguration
方法直接作修改。這兩個方法的配合,可使得咱們隊設備的修改是以一個 group 的方式提交,從而儘可能避免視覺或者狀態上的不一致性。在調用了 beginConfiguration
以後,咱們能夠增長或刪除輸出端,修改 sessionPreset
值,單獨配置視頻捕獲的輸入或輸出參數。知道咱們調用了 commitConfiguration
,這些修改採用被提交併一塊兒應用。
[session beginConfiguration]; // Remove an existing capture device. // Add a new capture device. // Reset the preset. [session commitConfiguration];
錄製過程當中 session 會發出通知來告知其對應的狀態,好比 session 開始、結束、被打斷。咱們能夠從 AVCaptureSessionRuntimeErrorNotification
來接收 session 運行時的錯誤。咱們也能夠差選 session 的運行時屬性來獲取其狀態是在運行中仍是被打斷。此外,這些屬性都是支持 KVO 監測的,而且通知會被髮送到主線程。
AVCaptureDevice
是由咱們現實中物理的提供輸入數據(好比音頻或視頻輸入)的設備抽象而來,每一個 AVCaptureDevice
對象都對應着一個輸入設備,好比咱們常見的前置攝像頭、後置攝像頭、麥克風。它們採集的數據將會輸出給 AVCaptureSession
實例。
咱們可使用 AVCaptureDevice
的 devices
和 devicesWithMediaType:
類方法來檢查哪些是當前可用的設備。若是須要,咱們還能夠獲取設備支持哪些功能。當前可用的設備列表是會動態變化的,有些設備會由於被別的應用使用而變得不可用,有的設備也有可能忽然就可用了,因此咱們須要註冊 AVCaptureDeviceWasConnectedNotification
和 AVCaptureDeviceWasDisconnectedNotification
通知來感知當前可用設備的變化狀況。
咱們可使用 capture input 來想向一個 AVCaptureSession
中添加輸入設備。
咱們能夠查詢採集設備的不一樣特性。好比,咱們可使用 hasMediaType:
接口來判斷採集設備是否支持某種媒體類型,也可使用 supportsAVCaptureSessionPreset:
接口來判斷採集設備是否支持預設的 session preset。咱們還能獲取設備的位置、本地化命名等信息以便於展現給用戶。
下面的示例代碼展現瞭如何遍歷設備,並打印設備名:
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。
不一樣的設備有不一樣的能力,好比有些設備支持不一樣的對焦或刷新模式。下面的代碼展現瞭如何找到一個支持手電筒以及預設的 preset 的設備。
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; NSMutableArray *torchDevices = [[NSMutableArray alloc] init]; for (AVCaptureDevice *device in devices) { [if ([device hasTorch] && [device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) { [torchDevices addObject:device]; } }
若是你找到多個設備,你能夠向用戶展現設備的 localizedName
來讓用戶選擇他們須要的設備。在使用對應的設備時,咱們能夠設置設備的各類工做模式,可是有一點須要注意的是在設置前須要對設備加鎖。
目前支持的聚焦模式有如下幾種:
AVCaptureFocusModeLocked
:聚焦點固定。一般以拍攝場景的中點做爲焦點。AVCaptureFocusModeAutoFocus
:自動聚焦。這種模式可讓用戶選擇一件事物來進行對焦,即便對於的位置不是拍攝場景的中點。AVCaptureFocusModeContinuousAutoFocus
:相機將持續自動對焦。咱們可使用 isFocusModeSupported:
接口來檢查設備是否支持相應的聚焦模式,而後設置對應的 focusMode
屬性。
咱們還能用 focusPointOfInterestSupported
來檢查設備是否支持指定焦點,若是支持,咱們就能夠設置對應的 focusPointOfInterest
屬性。其中 {0, 0}
表示左上角,{1, 1}
表示右下角。
咱們能夠訪問 adjustingFocus
屬性來獲知相機是否正在對焦,這個屬性是支持 KVO 的,因此咱們能夠監測它來獲知對焦狀態的變化。
若是你修改過了相機的對焦模式相關的設置,你能夠用下面的代碼將其恢復到默認狀態:
if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f); [currentDevice setFocusPointOfInterest:autofocusPoint]; [currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; }
目前支持的曝光模式有如下幾種:
AVCaptureExposureModeLocked
,曝光等級鎖定。AVCaptureExposureModeAutoExpose
,相機自動根據狀況調整一次曝光等級,而後將曝光模式切換到 AVCaptureExposureModeLocked
。AVCaptureExposureModeContinuousAutoExposure
,相機隨時根據狀況調整曝光等級。AVCaptureExposureModeCustom
,用戶自定義曝光等級。咱們能夠經過 isExposureModeSupported:
來檢查設備是否支持對應的模式,而後設置對應的 exposureMode
屬性。
咱們還能用 exposurePointOfInterestSupported
來檢查設備是否支持指定曝光點,若是支持,咱們就能夠設置對應的 exposurePointOfInterest
屬性。其中 {0, 0}
表示左上角,{1, 1}
表示右下角。
咱們能夠訪問 adjustingExposure
屬性來獲知相機是否正在改變曝光設置,這個屬性是支持 KVO 的,因此咱們能夠監測它來獲知曝光狀態的變化。
若是你修改過了相機的曝光模式相關的設置,你能夠用下面的代碼將其恢復到默認狀態:
if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) { CGPoint exposurePoint = CGPointMake(0.5f, 0.5f); [currentDevice setExposurePointOfInterest:exposurePoint]; [currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; }
目前支持的閃光燈模式有以下幾種:
AVCaptureFlashModeOff
,不會閃光。AVCaptureFlashModeOn
,會閃光。AVCaptureFlashModeAuto
,根據具體狀況決定是否閃光。咱們能夠經過 hasFlash
檢查設備是否有閃光燈,能夠經過 isFlashModeSupported:
檢查設備是否支持對應的模式,經過 flashMode
設置對應的模式。
在手電筒模式下,閃光燈會以低耗電量模式持續打開來爲圖像錄製來照明。目前支持的模式有如下幾種:
AVCaptureTorchModeOff
,關閉。AVCaptureTorchModeOn
,開啓。AVCaptureTorchModeAuto
,自動。咱們能夠經過 hasTorch
檢查設備是否有閃光燈,能夠經過 isTorchModeSupported:
檢查設備是否支持對應的手電筒模式,經過 torchMode
設置對應的模式。
視頻防抖主要依賴於硬件,雖然如此,也不是全部的視頻格式和分辨率都能支持。此外,開啓防抖也會帶來爲視頻錄製帶來必定的延遲。咱們能夠經過 videoStabilizationEnabled
來檢查是否啓動了防抖,經過 enablesVideoStabilizationWhenAvailable
來容許應用在條件支持的狀況下自動開啓防抖。
目前支持面幾種白平衡模式:
AVCaptureWhiteBalanceModeLocked
,固定模式。AVCaptureWhiteBalanceModeAutoWhiteBalance
,自動模式。相機根據狀況調整一次白平衡,而後切換至 AVCaptureWhiteBalanceModeLocked
模式。AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance
,相機隨時根據狀況調整白平衡。咱們能夠經過 isWhiteBalanceModeSupported:
檢查是否支持給定的模式,而後經過 whiteBalanceMode
設置對應模式。
咱們能夠訪問 adjustingWhiteBalance
屬性來獲知相機是否正在改變白平衡設置,這個屬性是支持 KVO 的,因此咱們能夠監測它來獲知白平衡狀態的變化。
咱們能夠經過 AVCaptureConnection
實例來設置想要在 AVCaptureOutput(AVCaptureMovieFileOutput, AVCaptureStillImageOutput, AVCaptureVideoDataOutput) 得到的圖像方向。
咱們經過 isVideoOrientationSupported
檢查是否支持改變視頻方向,經過 videoOrientation
設置方向。
下面是示例代碼:
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.
你應該只在但願設備的設置不能被修改時保持設備鎖,不正確的持有設備鎖可能會影響其餘程序的錄製質量。
有時候你可能須要在使用時切換設備,好比切換先後攝像頭。這時爲了不卡頓或者閃屏,咱們能夠從新配置一個正在執行的 session,可是咱們在修改先後須要分別使用 beginConfiguration
和 commitConfiguration
:
AVCaptureSession *session = <#A capture session#>; [session beginConfiguration]; [session removeInput:frontFacingCameraDeviceInput]; [session addInput:backFacingCameraDeviceInput]; [session commitConfiguration];
當最外的 commitConfiguration
被調用時,全部的配置修改會被一塊兒提交,這樣能夠保證平滑的切換。
咱們能夠用 AVCaptureDeviceInput
來給一個 session 添加設備。AVCaptureDeviceInput
是用來管理設備的端口。
NSError *error; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (!input) { // Handle the error appropriately. }
咱們用 addInput:
來添加設備。咱們還能夠在添加前用 canAddInput:
來檢查一下。
AVCaptureSession *captureSession = <#Get a capture session#>; AVCaptureDeviceInput *captureDeviceInput = <#Get a capture device input#>; if ([captureSession canAddInput:captureDeviceInput]) { [captureSession addInput:captureDeviceInput]; } else { // Handle the failure. }
AVCaptureInput
聲明一個或者多個媒體數據流。例如,輸入設備能夠同時提供音頻和視頻數據。輸入提供的每一個媒體流都被一個 AVCaptureInputPort
所表示。一個會話使用 AVCaptureConnection
對象來定義一組 AVCaptureInputPort
對象和一個 AVCaptureOutput
之間的映射。
要從一個 capture session 中獲取輸出,咱們須要給它添加一個或多個輸出實例,輸出實例都是 AVCaptureOutput
的子類。好比:
AVCaptureMovieFileOutput
,輸出電影文件。AVCaptureVideoDataOutput
,當咱們須要處理錄製獲得的視頻幀時(好比建立自定義的渲染層),能夠用這個。AVCaptureAudioDataOutput
,當咱們須要處理錄製到的音頻數據時,能夠用這個。AVCaptureStillImageOutput
,當咱們須要獲取靜態圖片以及對應的 metadata 時,能夠用這個。咱們可使用 addOutput:
向 session 中添加輸出實例,咱們也能夠在加以前,經過 canAddOutput:
來檢測要添加的輸出實例是不是兼容的。咱們能夠往一個正在運行狀態的 session 中添加輸出實例。
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
將音視頻數據保存到文件中時,咱們能夠作一些輸出設置,好比最大錄製時長、最大文件尺寸等等。咱們還可以在硬盤空間不夠時中止錄製。
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#>;
錄製時輸出的分辨率和碼率是根據 capture session 的 sessionPreset
屬性而定的。其中視頻的編碼類型是 H.264,音頻的編碼類型是 AAC,這個也是由不一樣的設備類型決定的。
咱們經過 startRecordingToOutputFileURL:recordingDelegate:
來開始錄製,這時你須要提供一個文件 URL 和一個 delegate。這個 URL 不能指向已存在的文件,由於這裏的音視頻輸出不會去覆蓋已有的資源。同時咱們也必需要有對這個 URL 指向位置的寫權限。這裏的 delegate 必須遵循 AVCaptureFileOutputRecordingDelegate
協議,並且必須實現 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#>];
在 captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:
方法中,delegate 能夠將最終輸出的文件寫入相冊,同時也須要檢查和處理各類可能出現的錯誤。
要確保文件是否成功,咱們能夠在 captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:
方法中檢查 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...
咱們須要檢查 error 的 userInfo
的 AVErrorRecordingSuccessfullyFinishedKey
對應的值,由於有時候即便咱們收到了錯誤,文件也多是保存成功的,這時候,有多是觸及了咱們設置的某些限制,好比 AVErrorMaximumDurationReached
或者 AVErrorMaximumFileSizeReached
,其餘的緣由還有:
AVErrorDiskFull
,磁盤空間不夠。AVErrorDeviceWasDisconnected
,錄製設備斷開鏈接。AVErrorSessionWasInterrupted
,錄製 session 被打斷了,好比來電話了。咱們能夠在任什麼時候候給錄製的文件設置 metadata,即便是在錄製過程當中。文件的 metadata 是由一系列的 AVMetadataItem
對象表示,咱們能夠用 AVMutableMetadataItem
來建立咱們本身的 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;
AVCaptureVideoDataOutput
對象使用代理來對外暴露視頻幀,咱們經過 setSampleBufferDelegate:queue:
來設置代理。除了設置代理之外,還須要設置一個 serial queue 來供代理調用,這裏必須使用 serial queue 來保證傳給 delegate 的幀數據的順序正確。咱們可使用這個 queue 來分發和處理視頻幀,這裏能夠參考一下例子 SquareCam。
captureOutput:didOutputSampleBuffer:fromConnection:
中輸出的視頻幀數據是用 CMSampleBufferRef
來表示的。默認狀況下,這個 buffer 的數據格式來自於相機能最高效處理的格式。咱們能夠經過 videoSettings
來設置一個自定義的輸出格式,這個值是一個字典,如今支持的鍵包括 kCVPixelBufferPixelFormatTypeKey
。推薦的 pixel formats 能夠經過 availableVideoCVPixelFormatTypes
屬性得到,此外還能經過 availableVideoCodecTypes
得到支持編碼類型。Core Graphics 和 OpenGL 都能很好地處理 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]; }
對於咱們的應用,設置夠用的分辨率就行了,不要過高,那樣性能會下降。
咱們須要確保,在 captureOutput:didOutputSampleBuffer:fromConnection:
中處理視頻幀的時間開銷不要超過度配給一幀的處理時長。若是這裏處理的太長,那麼 AV Foundation 將會中止發送視頻幀給代理,也會中止發給其餘輸出端,好比 preview layer。
你可使用 AVCaptureVideoDataOutput
的 minFrameDuration
屬性來確保你有足夠的時間來處理一幀。同時,咱們還能夠設置 alwaysDiscardsLateVideoFrames
爲 YES 來確保晚到的幀會被丟掉來避免延遲。若是你不介意延遲,而更想要處理更多的幀,那就設置這個值爲 NO,可是這並不意味着不會丟幀,只是不會被那麼早或那麼高效的丟掉。
當咱們想要截取帶着 metadata 的靜態圖片時,咱們能夠用 AVCaptureStillImageOutput
。截出的圖的分辨率依賴於 session 的 preset 和具體的設備。
不一樣的類型支持不一樣的圖片格式。咱們能夠用 availableImageDataCVPixelFormatTypes
和 availableImageDataCodecTypes
來檢查當前的設備支持的像素類型和編碼類型。而後,咱們能夠經過設置 outputSettings
屬性指定圖片的格式。
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = @{AVVideoCodecKey: AVVideoCodecJPEG}; [stillImageOutput setOutputSettings:outputSettings];
若是咱們要截取一張 JPEG 圖片,你最好不要指定你本身的壓縮格式,相反,你須要作的是讓 AVCaptureStillImageOutput
幫你作壓縮,由於它使用硬件加速來壓縮。若是這時你須要圖像的數據,能夠用 jpegStillImageNSDataRepresentation:
來獲取對應的 NSData
對象,這個數據是未經壓縮的,甚至你均可以修改它。
當截圖時,咱們使用 captureStillImageAsynchronouslyFromConnection:completionHandler:
方法,其中的第一個參數是對應的 connection,這是你須要查找一下哪一個 connection 的輸出端口是在手機視頻:
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; } }
第二個參數是一個 block 回調,其中帶回兩個參數:一個包含圖像數據的 CMSampleBuffer
,一個 NSError
。這個 sample buffer 中可能包含着 metadata,好比一個 EXIF 字典做爲附屬信息。咱們能夠去修改附屬信息,可是須要注意針對 JPEG 圖像的像素和編碼上的優化。
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) { CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL); if (exifAttachments) { // Do something with the attachments. } // Continue as appropriate. }];
咱們能夠用 AVCaptureVideoPreviewLayer
來給用戶提供預覽,咱們不須要任何 output 對象來展現預覽。此外,咱們可使用 AVCaptureVideoDataOutput
來在給用戶預覽以前獲取到圖像像素級別的數據。
Unlike a capture output, a video preview layer maintains a strong reference to the session with which it is associated. This is to ensure that the session is not deallocated while the layer is attempting to display video. This is reflected in the way you initialize a preview layer:
不像 capture output,一個 video preview layer 會持有一個關聯 session 的強引用。這樣來防止 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
對象同樣,咱們能夠縮放它的圖像,執行變換,旋轉。一個不一樣的地方是,咱們須要設置 layer 的 orientation
屬性來指定它改如何旋轉從相機得到的圖像。此外,咱們能夠用 supportsVideoMirroring
來測試設備是否支持視頻鏡面效果(左右翻轉),咱們能夠設置 videoMirrored
來配置是否開啓鏡面效果,即便 automaticallyAdjustsVideoMirroring
被設置爲默認的 YES 也沒問題(這個值是在配置 session 時被自動設置的)。
咱們能夠經過 videoGravity
屬性設置視頻重力模式,好比:
AVLayerVideoGravityResizeAspect
,保持視頻的寬高比;不必定徹底填充,可能留黑邊;不裁剪視頻,。AVLayerVideoGravityResizeAspectFill
,保持視頻的寬高比;徹底填充,不留黑邊;可能裁剪視頻。AVLayerVideoGravityResize
,不保持視頻寬高比,可能扭曲畫面;徹底填充;不裁剪視頻。須要注意的是,在實現點擊聚焦時必須考慮到該層的預覽方向和重力,並考慮預覽變爲鏡像顯示的可能性。能夠參考實例項目:AVCam-iOS: Using AVFoundation to Capture Images and Movies。
To monitor the average and peak power levels in an audio channel in a capture connection, you use an AVCaptureAudioChannel object. Audio levels are not key-value observable, so you must poll for updated levels as often as you want to update your user interface (for example, 10 times a second).
要監測 capture connection 中音頻通道的平均強度和峯值強度,咱們能夠用 AVCaptureAudioChannel
。音頻等級是不支持 KVO 監測的,因此當咱們想更新用戶界面時,咱們須要去查詢。
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
。大概包括下面幾步:
AVCaptureSession
對象來協調輸入設備到輸出的數據流。AVCaptureDevice
。AVCaptureDeviceInput
對象。AVCaptureVideoDataOutput
來輸出視頻幀。AVCaptureVideoDataOutput
的 delegate
方法來處理視頻幀。CMSampleBuffer
轉爲 UIImage
。代碼以下:
// 建立並配置 Capture Session: AVCaptureSession *session = [[AVCaptureSession alloc] init]; session.sessionPreset = AVCaptureSessionPresetMedium; // 建立和配置 Device 和 Device Input: AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; NSError *error = nil; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (!input) { // Handle the error appropriately. } [session addInput:input]; // 建立和配置 Video Data Output: AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init]; [session addOutput:output]; output.videoSettings = @{(NSString *) kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}; output.minFrameDuration = CMTimeMake(1, 15); // 設置 Video Data Output 的 delegate 和處理隊列: dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL); [output setSampleBufferDelegate:self queue:queue];
代理方法實現:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { UIImage *image = imageFromSampleBuffer(sampleBuffer); // Add your code here that uses the image. }
開始錄製:
錄製前,還須要注意申請相機權限:
// 申請權限: 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];
iOS 7 之後在一些設備中引入了高幀率視頻捕獲的支持。咱們能夠經過 AVCaptureDeviceFormat
來查詢設備支持的媒體類型、幀率、縮放比、視頻防抖等。在 AVFoundation 中:
須要注意的是,如今最新的 iOS 版本支持的能力會比以上這些更多更強大。
播放時,能夠經過 AVPlayer
設置 rate
屬性來設置播放速率。
AVPlayerItem
能夠支持經過 audioTimePitchAlgorithm
屬性來設置當你用不一樣的速率播放視頻時,該如何播放音頻。如下是相關選項:
AVAudioTimePitchAlgorithmLowQualityZeroLatency
,較低音質,適應多種播放速率。適宜的速率:0.5, 0.666667, 0.8, 1.0, 1.25, 1.5, 2.0.AVAudioTimePitchAlgorithmTimeDomain
,普通音質。適宜的速率:0.5–2x。AVAudioTimePitchAlgorithmSpectral
,最高音質,性能消耗較大。適宜的速率:1/32–32。AVAudioTimePitchAlgorithmVarispeed
,高音質。適宜的速率:1/32–32。一般使用 AVMutableComposition
來作音視頻編輯:
composition
類方法類建立 AVMutableComposition
對象。insertTimeRange:ofAsset:atTime:error:
來插入音視頻數據。scaleTimeRange:toDuration:
來設置音視頻數據的時間區間。可使用 AVAssetExportSession
來導出 60 fps 的視頻,能夠用如下兩種方式:
AVAssetExportPresetPassthrough
preset 來編碼從新編碼視頻。它會從新處理那些標記爲 60 fps、降速、加速區域的時間。frameDuration
爲 30 fps。好比設置 export session 的 audioTimePitchAlgorithm
來配置音頻播放選項。咱們用 AVCaptureMovieFileOutput
來錄製高幀率的視頻,它會默認支持高幀率的視頻錄製,會默認選擇正確的 H264 pitch level 和比特率。
若是想要作一些自定義錄製,咱們必須使用 AVAssetWriter
,這個則須要一些額外的建立過程。
一般咱們須要設置一下:
assetWriterInput.expectsMediaDataInRealTime = YES;
來保證視頻捕獲能跟得上傳入的數據。