視訊採集:iOS平臺基於AVCaptureDevice的實現
前言
這篇文章簡單介紹下移動端iOS系統下利用AVCaptureDevice
進行視訊資料採集的方法。
按照慣例先上一份原始碼:iOSVideo
攝像頭採集相關核心實現在:NTVideoCapture.m
官方文件可以參考:AVFoundation官方文件
PS:採集部分的邏輯會相對比較簡單,後續會在視訊的採集基礎上面介紹怎麼利用OpenGL去繪製採集獲取到的資料。
入門知識
AVCaptureSession
在iOS平臺開發中只要跟硬體相關的都要從會話開始進行配置,如果我們使用攝像頭的話可以利用AVCaptureSession
進行視訊採集,其可以對輸入和輸出資料進行管理,負責協調從哪裡採集資料,輸出到哪裡去。
AVCaptureDevice
一個AVCaptureDevice
對應的是一個物理採集裝置,我們可以通過該物件來獲取和識別裝置屬性。
例如通過AVCaptureDevice.position
檢測其攝像頭的方向。
AVCaptureInputAVCaptureInput
是一個抽象類,AVCaptureSession
的輸入端必須是AVCaptureInput
的實現類。
例如利用AVCaptureDevice
構建AVCaptureDeviceInput
作為採集裝置輸入端。
AVCaptureOutputAVCaptureOutput
是一個抽象類,AVCaptureSession
的輸出端必須是AVCaptureOutput
的實現類。
例如AVCaptureVideoDataOutput
可以作為一個原始視訊資料的輸出端。
AVCaptureConnectionAVCaptureConnection
是AVCaptureSession
用來建立和維護AVCaptureInput
和AVCaptureOutput
之間的連線的,一個AVCaptureSession
可能會有多個AVCaptureConnection
例項。
採集步驟
- 建立
AVCaptureSession
並初始化。 - 通過前後置攝像頭找到對應的
AVCaptureDevice
。 - 通過
AVCaptureDevice
建立輸入端AVCaptureDeviceInput
,並將其新增到AVCaptureSession
的輸入端。 - 建立輸出端
AVCaptureVideoDataOutput
,並進行Format和Delgate的配置,最後新增到AVCaptureSession
的輸出端。 - 獲取
AVCaptureConnection
,並進行相應的引數設定。 - 呼叫
AVCaptureSession
的startRunning
和stopRunning
設定採集狀態。
配置會話
建立一個AVCaptureSession
很簡單:
AVCaptureSession *captureSession;
captureSession = [[AVCaptureSession alloc] init];
我們可以在AVCaptureSession
來配置指定所需的影象質量和解析度,可選引數請參考AVCaptureSessionPreset.h
。
在設定前需要檢測是否支援該Preset是否被支援:
//指定採集1280x720解析度大小格式
AVCaptureSessionPreset preset = AVCaptureSessionPreset1280x720;
//檢查AVCaptureSession是否支援該AVCaptureSessionPreset
if ([captureSession canSetSessionPreset:preset]) {
captureSession.sessionPreset = preset;
}
else {
//錯誤處理,不支援該AVCaptureSessionPreset型別值
}
配置輸入端
通過AVCaptureDevice
的devicesWithMediaType
的方法來獲取攝像頭,由於iOS存在多個攝像頭,所以這裡一般返回一個裝置的陣列。
根據業務需要(例如前後置攝像頭),我們找到其中對應的AVCaptureDevice
,並將其構造成AVCaptureDeviceInput
例項。
AVCaptureDevice *device;
AVCaptureDeviceInput *captureInput;
//獲取前後置攝像頭的標識
AVCaptureDevicePosition position = _isFront ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;
//獲取裝置的AVCaptureDevice列表
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *item in devices) {
//如果找到對應的攝像頭
if ([item position] == position) {
device = item;
break;
}
}
if (device == nil) {
//錯誤處理,沒有找到對應的攝像頭
}
//建立AVCaptureDeviceInput輸入端
captureInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:nil];
配置輸出端
如果我們想要獲取到攝像頭採集到的原始視訊資料的話,需要配置一個AVCaptureVideoDataOutput
作為AVCaptureSession
的輸出端,我們需要給其設定採集的視訊格式和採集資料回撥佇列。
AVCaptureVideoDataOutput *captureOutput;
//建立一個輸出端AVCaptureVideoDataOutput例項
captureOutput = [[AVCaptureVideoDataOutput new];
//配置輸出的資料格式
[captureOutput setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8PlanarFullRange)}];
//設定輸出代理和採集資料的佇列
dispatch_queue_t outputQueue = dispatch_queue_create("ACVideoCaptureOutputQueue", DISPATCH_QUEUE_SERIAL);
[captureOutput setSampleBufferDelegate:self queue:outputQueue];
// 丟棄延遲的幀
captureOutput.alwaysDiscardsLateVideoFrames = YES;
需要注意的幾個點
- 對於
setVideoSettings
,雖然AVCaptureVideoDataOutput
提供的是一個字典設定,但是現在只支援kCVPixelBufferPixelFormatTypeKey
這個key。 - 畫素格式預設使用的是
YUVFullRange
型別,表示其YUV取值範圍是0~255,而還有另外一種型別YUVVideoRange
型別則是為了防止溢位,將YUV的取值範圍限制為16~235。 -
setSampleBufferDelegate
必須指定序列佇列來確保視訊資料獲取委託呼叫的正確順序,當然你也可以修改佇列來設定視訊處理的優先順序別。 -
alwaysDiscardsLateVideoFrames = YES
可以在你沒有足夠時間處理視訊幀時丟棄任何延遲的視訊幀而不是等待處理,如果你設定了NO並不能保證幀不會被丟棄,只是他們不會被提前有意識的丟棄而已。
配置會話的輸入和輸出
//新增輸入裝置到會話
if ([captureSession canAddInput:captureInput]) {
[captureSession addInput:captureInput];
}
//新增輸出裝置到會話
if ([captureSession canAddOutput:captureOutput]) {
[captureSession addOutput:captureOutput];
}
//獲取連線並設定視訊方向為豎屏方向
AVCaptureConnection *conn = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
conn.videoOrientation = AVCaptureVideoOrientationPortrait;
//前置攝像頭採集到的資料本來就是映象翻轉的,這裡設定為映象把畫面轉回來
if (device.position == AVCaptureDevicePositionFront && conn.supportsVideoMirroring) {
conn.videoMirrored = YES;
}
如果AVCaptureSession
已經開啟了採集,如果這個時候需要修改解析度、輸入輸出等配置。那麼需要用到beginConfiguration
和commitConfiguration
方法把修改的程式碼包圍起來,也就是先呼叫beginConfiguration
啟動事務,然後配置解析度、輸入輸出等資訊,最後呼叫commitConfiguration
提交修改;這樣才能確保相應修改作為一個事務組提交,避免狀態的不一致性。
AVCaptureSession
管理了採集過程中的狀態,當開始採集、停止採集、出現錯誤等都會發起通知,我們可以監聽通知來獲取AVCaptureSession
的狀態,也可以呼叫其屬性來獲取當前AVCaptureSession
的狀態,值得注意一點是AVCaptureSession
相關的通知都是在主執行緒的。
開始採集資料和資料回撥
當上面的配置搞定後,呼叫startRunning
就可以開始資料的採集了。
if (![captureSession isRunning]) {
[captureSession startRunning];
}
停止採集只需要呼叫stopRunning
方法即可。
if ([captureSession isRunning]) {
[captureSession stopRunning];
}
對於採集回撥的視訊資料,會在[captureOutput setSampleBufferDelegate:self queue:outputQueue]
設定的代理方法觸發返回,
其中最重要的是CMSampleBufferRef
,其中實際儲存著攝像頭採集到的影象。
方法原型如下:
- (void)captureOutput:(AVCaptureOutput *)output
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
切換前後攝像頭
在視訊採集的過程中,我們經常需要切換前後攝像頭,這裡我們也就是需要把AVCaptureSession
的輸入端改為對應的攝像頭就可以了。
當然我們可以用beginConfiguration
和commitConfiguration
將修改邏輯包圍起來,也可以先呼叫stopRunning
方法停止採集,然後重新配置好輸入和輸出,再呼叫startRunning
開啟採集。
//獲取攝像頭列表
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
//獲取當前攝像頭方向
AVCaptureDevicePosition currentPosition = captureInput.device.position;
//轉換攝像頭
if (currentPosition == AVCaptureDevicePositionBack){
currentPosition = AVCaptureDevicePositionFront;
}
else{
currentPosition = AVCaptureDevicePositionBack;
}
//獲取到新的AVCaptureDevice
NSArray *captureDeviceArray = [devices filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", currentPosition]];
AVCaptureDevice *device = captureDeviceArray.firstObject;
//開始配置
[captureSession beginConfiguration];
//構造一個新的AVCaptureDeviceInput的輸入端
AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//移除掉就的AVCaptureDeviceInput
[captureSession removeInput:captureInput];
//將新的AVCaptureDeviceInput新增到AVCaptureSession中
if ([captureSession canAddInput:newInput]){
[captureSession addInput:newInput];
captureInput = newInput;
}
//提交配置
[captureSession commitConfiguration];
//重新獲取連線並設定視訊的方向、是否映象
AVCaptureConnection *conn = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
conn.videoOrientation = AVCaptureVideoOrientationPortrait;
if (device.position == AVCaptureDevicePositionFront && conn.supportsVideoMirroring){
conn.videoMirrored = YES;
}
視訊幀率
iOS預設的幀率設定是30幀,如果我們的業務場景不需要用到30幀,或者我們的處理能力達不到33ms(1000ms/30幀)的話,我們可以通過設定修改視訊的輸出幀率:
NSInteger fps = 15;
//獲取設定支援設定的幀率範圍
AVFrameRateRange *fpsRange = [captureInput.device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0];
if (fps > fpsRange.maxFrameRate || fps < fpsRange.minFrameRate) {
//不支援該fps設定
return;
}
// 設定輸入的幀率
captureInput.device.activeVideoMinFrameDuration = CMTimeMake(1, (int)fps);
captureInput.device.activeVideoMaxFrameDuration = CMTimeMake(1, (int)fps);
簡易預覽
如果不想通過自己實現OpenGL
渲染採集到的視訊幀,當然,iOS也提供了一個預覽元件AVCaptureVideoPreviewLayer
,其繼承於CALayer
。
可以將這個layer新增到UIView
上面就可以實現採集到的視訊的實時預覽。
//建立一個AVCaptureVideoPreviewLayer,並將AVCaptureSession傳入
AVCaptureVideoPreviewLayer *previewLayer;
previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:captureSession];
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewLayer.frame = self.view.bounds;
//將其載入到UIView上面即可
[self.view.layer addSublayer:previewLayer];
PS:如果採用AVCaptureVideoPreviewLayer
進行視訊預覽的話,那麼可以不配置AVCaptureSession
的輸出端相關。
結語
這篇文章簡單介紹下移動端iOS系統下利用AVCaptureDevice
進行視訊資料採集的方法,並提供了相關程式碼的使用示例。
限於篇幅就不對閃光燈、對焦等展開介紹,詳細請參考官方文件。
後續文章將介紹怎麼利用OpenGL來渲染攝像頭採集到的視訊幀。
End!
相關文章
- iOS視訊採集實戰(AVCaptureSession)iOSAPTSession
- iOS視訊流採集概述(AVCaptureSession)iOSAPTSession
- 基於 Agora SDK 實現 iOS 端的多人視訊互動GoiOS
- 轉載:iOS音視訊實時採集硬體編碼iOS
- 短視訊平臺原始碼,IOS圖文混排基礎原始碼iOS
- [平臺建設] 大資料平臺如何實現任務日誌採集大資料
- 自媒體採集平臺免費,免費的自媒體採集平臺
- 如何實現視訊加密全平臺播放加密
- 出行平臺採集機票價格資訊
- iOS採集錄製音視訊API選擇推薦iOSAPI
- 視訊私有云實戰:基於Docker構建點播私有云平臺Docker
- iOS 實時音訊採集與播放Audio Unit使用iOS音訊
- 基於Flutter實現的仿開眼視訊AppFlutterAPP
- 基於Android平臺實現人臉識別Android
- 自媒體素材採集平臺,採集影片文章素材
- 能耗監測平臺實現可以採集IEC104電錶嗎
- Electron實現跨平臺全能視訊播放器播放器
- 遊戲平臺採集資料遊戲
- 在 iOS 平臺實現Ping 和 tracerouteiOS
- 實現基於zoom平臺上的oss額外儲存OOM
- 自媒體素材採集平臺,素材採集方法都有這些
- BizWorks應⽤平臺基於KubeVela的實踐
- Android 音視訊採集那些事Android
- 電商平臺資料採集介面
- 基於 Electron 做視訊會議的兩種實現方式
- 基於 WebRTC 和 WebVR 實現 VR 視訊通話WebVR
- 基於 Springboot+layui 實現介面自動化平臺Spring BootUI
- 視訊分享平臺
- 人員基礎資訊採集
- iOS 音量柱的實現(mic 採集的聲音DB反映成音量柱)iOS
- 攜程基於Flink的實時特徵平臺特徵
- BizWorks 應用平臺基於 KubeVela 的實踐
- 基於 Agora SDK 實現 Windows 端的多人視訊互動(基於3.6.2版本)GoWindows
- 基於.NET 5實現的開源通用許可權管理平臺
- 一鍵自動採集商品圖片 視訊,支援亞馬遜、速賣通等跨境平臺亞馬遜
- 基於聲網 Flutter SDK 實現多人視訊通話Flutter
- AR實踐:基於ARKit實現電影中的全息視訊會議
- 基於環信實現實時視訊語音通話功能