如何快速的開發一個完整的iOS直播app(採集篇)
作者:袁崢
連結:https://www.jianshu.com/p/c71bfda055fa
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
開發一款直播app,首先需要採集主播的視訊和音訊,然後傳入流媒體伺服器,本篇主要講解如何採集主播的視訊和音訊,當前可以切換前置後置攝像頭和焦點游標
,但是美顏功能還沒做,可以看見素顏的你,後續還會有直播的其他功能文章陸續釋出。
基本知識介紹
-
AVFoundation
: 音視訊資料採集需要用AVFoundation框架. -
AVCaptureDevice
:硬體裝置,包括麥克風、攝像頭,通過該物件可以設定物理裝置的一些屬性(例如相機聚焦、白平衡等) -
AVCaptureDeviceInput
:硬體輸入物件,可以根據AVCaptureDevice建立對應的AVCaptureDeviceInput物件,用於管理硬體輸入資料。 -
AVCaptureOutput
:硬體輸出物件,用於接收各類輸出資料,通常使用對應的子類AVCaptureAudioDataOutput(聲音資料輸出物件)、AVCaptureVideoDataOutput(視訊資料輸出物件) -
AVCaptionConnection
:當把一個輸入和輸出新增到AVCaptureSession之後,AVCaptureSession就會在輸入、輸出裝置之間建立連線,而且通過AVCaptureOutput可以獲取這個連線物件。 -
AVCaptureVideoPreviewLayer
:相機拍攝預覽圖層,能實時檢視拍照或視訊錄製效果,建立該物件需要指定對應的AVCaptureSession物件,因為AVCaptureSession包含視訊輸入資料,有視訊資料才能展示。 -
AVCaptureSession
:協調輸入與輸出之間傳輸資料
- 系統作用:可以操作硬體裝置
- 工作原理:讓App與系統之間產生一個捕獲會話,相當於App與硬體裝置有聯絡了, 我們只需要把硬體輸入物件和輸出物件新增到會話中,會話就會自動把硬體輸入物件和輸出產生連線,這樣硬體輸入與輸出裝置就能傳輸音視訊資料。
- 現實生活場景:租客(輸入錢),中介(會話),房東(輸出房),租客和房東都在中介登記,中介就會讓租客與房東之間產生聯絡,以後租客就能直接和房東聯絡了。
捕獲音視訊步驟:官方文件
-
1.
建立AVCaptureSession物件 -
2.
獲取AVCaptureDevicel錄影裝置(攝像頭),錄音裝置(麥克風),注意不具備輸入資料功能,只是用來調節硬體裝置的配置。 -
3.
根據音訊/視訊硬體裝置(AVCaptureDevice)建立音訊/視訊硬體輸入資料物件(AVCaptureDeviceInput),專門管理資料輸入。 -
4.
建立視訊輸出資料管理物件(AVCaptureVideoDataOutput),並且設定樣品快取代理(setSampleBufferDelegate)就可以通過它拿到採集到的視訊資料 -
5.
建立音訊輸出資料管理物件(AVCaptureAudioDataOutput),並且設定樣品快取代理(setSampleBufferDelegate)就可以通過它拿到採集到的音訊資料 -
6.
將資料輸入物件AVCaptureDeviceInput、資料輸出物件AVCaptureOutput新增到媒體會話管理物件AVCaptureSession中,就會自動讓音訊輸入與輸出和視訊輸入與輸出產生連線. -
7.
建立視訊預覽圖層AVCaptureVideoPreviewLayer並指定媒體會話,新增圖層到顯示容器layer中 -
8.
啟動AVCaptureSession,只有開啟,才會開始輸入到輸出資料流傳輸。
// 捕獲音視訊
- (void)setupCaputureVideo
{
// 1.建立捕獲會話,必須要強引用,否則會被釋放
AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
_captureSession = captureSession;
// 2.獲取攝像頭裝置,預設是後置攝像頭
AVCaptureDevice *videoDevice = [self getVideoDevice:AVCaptureDevicePositionFront];
// 3.獲取聲音裝置
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
// 4.建立對應視訊裝置輸入物件
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
_currentVideoDeviceInput = videoDeviceInput;
// 5.建立對應音訊裝置輸入物件
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
// 6.新增到會話中
// 注意“最好要判斷是否能新增輸入,會話不能新增空的
// 6.1 新增視訊
if ([captureSession canAddInput:videoDeviceInput]) {
[captureSession addInput:videoDeviceInput];
}
// 6.2 新增音訊
if ([captureSession canAddInput:audioDeviceInput]) {
[captureSession addInput:audioDeviceInput];
}
// 7.獲取視訊資料輸出裝置
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
// 7.1 設定代理,捕獲視訊樣品資料
// 注意:佇列必須是序列佇列,才能獲取到資料,而且不能為空
dispatch_queue_t videoQueue = dispatch_queue_create("Video Capture Queue", DISPATCH_QUEUE_SERIAL);
[videoOutput setSampleBufferDelegate:self queue:videoQueue];
if ([captureSession canAddOutput:videoOutput]) {
[captureSession addOutput:videoOutput];
}
// 8.獲取音訊資料輸出裝置
AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];
// 8.2 設定代理,捕獲視訊樣品資料
// 注意:佇列必須是序列佇列,才能獲取到資料,而且不能為空
dispatch_queue_t audioQueue = dispatch_queue_create("Audio Capture Queue", DISPATCH_QUEUE_SERIAL);
[audioOutput setSampleBufferDelegate:self queue:audioQueue];
if ([captureSession canAddOutput:audioOutput]) {
[captureSession addOutput:audioOutput];
}
// 9.獲取視訊輸入與輸出連線,用於分辨音視訊資料
_videoConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
// 10.新增視訊預覽圖層
AVCaptureVideoPreviewLayer *previedLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
previedLayer.frame = [UIScreen mainScreen].bounds;
[self.view.layer insertSublayer:previedLayer atIndex:0];
_previedLayer = previedLayer;
// 11.啟動會話
[captureSession startRunning];
}
// 指定攝像頭方向獲取攝像頭
- (AVCaptureDevice *)getVideoDevice:(AVCaptureDevicePosition)position
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
if (device.position == position) {
return device;
}
}
return nil;
}
#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate
// 獲取輸入裝置資料,有可能是音訊有可能是視訊
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if (_videoConnection == connection)