iOS - 直播系列一:視頻採集

蘋果官方文檔-AVFoundationhtml

爲了管理從相機或者麥克風等這樣的設備捕獲到的信息,咱們須要輸入對象(input)和輸出對象(output),而且使用一個會話(AVCaptureSession)來管理 input 和 output 以前的數據流:git

類名 簡介
AVCaptureDevice 輸入設備,例如 攝像頭 麥克風
AVCaptureInput 輸入端口 [使用其子類]
AVCaptureOutput 設備輸出 [使用其子類],輸出視頻文件或者靜態圖像
AVCaptureSession 管理輸入到輸出的數據流
AVCaptureVideoPreviewLayer 展現採集 預覽View

如圖,經過單個 session,也能夠管理多個 input 和 output 對象之間的數據流,從而獲得視頻、靜態圖像和預覽視圖 github

多個輸入輸出設備

如圖,input 能夠有一個或多個輸入端口,output 也能夠有一個或多個數據來源(如:一個 AVCaptureMovieFileOutput 對象能夠接收視頻數據和音頻數據)緩存

當添加 input 和 output 到 session 中時,session 會自動創建起一個鏈接(AVCaptureConnection)。咱們可使用這個 connection 來設置從 input 或者 從 output 獲得的數據的有效性,也能夠用來監控在音頻信道中功率的平均值和峯值。bash

AVCaptureConnection

使用 Session 來管理數據流

建立一個 session 用來管理捕獲到的數據,須要先將 inputs 和 outputs 添加到 session 中,當 session 執行 [startRunning] 方法後就會開始將數據流發送至 session,經過執行[stopRunning] 方法來結束數據流的發送。微信

AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];

// 添加 inputs 和 outputs

[session startRunning];
複製代碼

在 [session startRunning] 以前咱們須要進行一些基本的配置 (如:設備分辨率,添加輸入輸出對象等)session

設置分辨率

// 設置分辨率 720P 標清
if ([captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
    captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
}
複製代碼

附蘋果官方文檔中可供配置的分辨率列表app

分辨率列表

其中高分辨率(AVCaptureSessionPresetHigh) 爲默認值,會根據當前設備進行自適應,可是這樣以後導出來的文件就會很大,通常狀況下設置爲標清(AVCaptureSessionPreset1280x720) 就能夠了異步

輸入對象

// 直接使用後置攝像頭
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
複製代碼
// 在這個方法中的 mediaType 有三個選項供咱們使用
// AVMediaTypeVideo 視頻
// AVMediaTypeAudio 音頻
// AVMediaTypeMuxed 混合(視頻 + 音頻)
+ (nullable AVCaptureDevice *)defaultDeviceWithMediaType:(AVMediaType)mediaType;
複製代碼

可是這種方式只能獲取到後置攝像頭,若是想要獲取前置攝像頭,可以使用async

AVCaptureDevice *videoDevice;
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
   if(device.position == AVCaptureDevicePositionFront) {
        // 前置攝像頭
        videoDevice = device;
   }
}
複製代碼
// 經過設備獲取輸入對象
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
// 給會話添加輸入
if([captureSession canAddInput:videoInput]) {
    [captureSession addInput:videoInput];
}
複製代碼

輸出對象

// 視頻輸出:設置視頻原數據格式:YUV, RGB 
// 蘋果不支持YUV的渲染,只支持RGB渲染,這意味着: YUV => RGB
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];

// videoSettings: 設置視頻原數據格式 YUV FULL
videoOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)};

// 設置代理:獲取幀數據
// 隊列:串行/並行,這裏使用串行,保證數據順序 
dispatch_queue_t queue = dispatch_queue_create("LinXunFengSerialQueue", DISPATCH_QUEUE_SERIAL);
[videoOutput setSampleBufferDelegate:self queue:queue];

// 給會話添加輸出對象
if([captureSession canAddOutput:videoOutput]) {
    // 給會話添加輸入輸出就會自動創建起鏈接
    [captureSession addOutput:videoOutput];
}
複製代碼

在這裏,輸出對象能夠設置幀率

// 幀率:1秒10幀就差很少比較流暢了
videoOutput.minFrameDuration = CMTimeMake(1, 10);
複製代碼

輸出對象在設置視頻原數據格式時使用 videoSettings 屬性,須要賦值的類型是字典 格式有兩種,一種是YUV,另外一種是RGB(通常咱們都使用YUV,由於體積比RGB小)

// key
kCVPixelBufferPixelFormatTypeKey 指定解碼後的圖像格式

// value
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange  : YUV420 用於標清視頻[420v]
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange   : YUV422 用於高清視頻[420f] 
kCVPixelFormatType_32BGRA : 輸出的是BGRA的格式,適用於OpenGL和CoreImage

區別:
一、前兩種是相機輸出YUV格式,而後轉成RGBA,最後一種是直接輸出BGRA,而後轉成RGBA;
二、420v 輸出的視頻格式爲NV12;範圍: (luma=[16,235] chroma=[16,240])
三、420f 輸出的視頻格式爲NV12;範圍: (luma=[0,255] chroma=[1,255])
複製代碼

預覽圖層

AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
previewLayer.frame = self.view.bounds;
[self.view.layer  addSublayer:previewLayer];
複製代碼

實時顯示攝像頭捕獲到的圖像,但不適用於濾鏡渲染

代理方法

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate
/* CMSampleBufferRef: 幀緩存數據,描述當前幀信息 CMSampleBufferGetXXX : 獲取幀緩存信息 CMSampleBufferGetDuration : 獲取當前幀播放時間 CMSampleBufferGetImageBuffer : 獲取當前幀圖片信息 */
// 獲取幀數據
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    // captureSession 會話若是沒有強引用,這裏不會獲得執行
    
    NSLog(@"----- sampleBuffer ----- %@", sampleBuffer);
}
複製代碼
// 獲取幀播放時間
CMTime duration = CMSampleBufferGetDuration(sampleBuffer);
複製代碼

在代理方法中,能夠把 sampleBuffer 數據渲染出來去顯示畫面。適用於濾鏡渲染

// 獲取圖片幀數據
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVImageBuffer:imageBuffer];
UIImage *image = [UIImage imageWithCIImage:ciImage];

dispatch_async(dispatch_get_main_queue(), ^{
    self.imageView.image = image;
});
複製代碼

須要注意的是:代理方法中的全部動做所在隊列都是在異步串行隊列中,因此更新UI的操做須要回到主隊列中進行!!

可是此時會發現,畫面是向左旋轉了90度,由於默認採集的視頻是橫屏的,須要咱們進一步作調整。如下步驟添加在[session startRunning];以前便可,可是必定要在添加了 input 和 output以後~

// 獲取輸入與輸出之間的鏈接
AVCaptureConnection *connection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
// 設置採集數據的方向
connection.videoOrientation = AVCaptureVideoOrientationPortrait;
// 設置鏡像效果鏡像
connection.videoMirrored = YES;
複製代碼

Demo

LXFAudioVideo

微信公衆號
相關文章
相關標籤/搜索