1. 程式人生 > 其它 >如何快速的開發一個完整的iOS直播app(採集篇)

如何快速的開發一個完整的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)