#直播總結
##1.概述
關于直播的技術文章不少,成體系的不多。我們將用這篇文章,更系統化地介紹當下大熱的視頻直播各環節的關鍵技術,幫助視頻直播創業者們更全面、深入地了解視頻直播技術,更好地技術選型。
###1.1 一個完整的直播APP原理
`直播原理` : 把主播錄制的視頻,推流送到服務器,服務器經過處理(鑒黃等),通過CDN分發給觀眾看。
`直播環節` : 推流端(采集、美顏、編碼、推流),服務端處理(轉碼、錄制、截圖、鑒黃)、播放器(拉流、解碼、渲染)、互動系統(聊天室、禮物系統、贊)
###1.2 一個完整直播APP實現流程
`1.采集、2.濾鏡處理、3.編碼、4.推流、5.CDN分發、6.拉流、7.解碼、8.播放、9.聊天互動`

###1.3 一個完整直播APP架構
`1.采集端、服務端、播放端`

###1.4 一個完整直播APP技術點

下面我們會選擇一部分技術進行講解。
`提示`:[總結不好 圖看不清 請點這里](http://www.lxweimin.com/p/bd42bacbe4cc)
##2. 視頻采集
###2.1 基本知識介紹
`AVFundation` : 音視頻數據采集需要用AVFundation框架
`AVCaptureDevice` : 硬件設備,包括麥克風、攝像頭、通過該對象可以設置物理設備的一些屬性。例如相機焦距,白平衡等
`AVCaptureDeviceInput` : 硬件輸入對象,可以根據AVCaptureDevice創建對應的AVCaptureDeviceInput對象,用于管理硬件輸入數據
`AVCaptureOutput` : 硬件輸出對象,用于接收各類輸出數據,通常使用對應的子類AVCaptureAudioDataOutput(聲音數據輸出對象), AVCaptureVideoDataOutput(視頻輸出對象)
`AVCaptureConnection` : 當把一個輸入和輸出添加到AVCaptureSession后。AVCaptureSession就會在輸出、輸出設備之間建立連接,而且通過AVCaptureOutput可以獲得這個對象
`AVCaptureVideoPreviewLayer` : 相機拍攝預覽圖層,能實時查看相機效果。創建該對象需要指定對應的AVCaptureSession對象,因為AVCaptureSession包含輸出數據,有視頻數據才能顯示。
`AVCaptureSession` : 協調輸入與輸出之間傳遞數據
###2.2 捕獲音視頻步驟
包含關系:

步驟:
>1.? 創建AVCaptureDevice(video或者audio)
>2.? 根據AVCaptureDevice創建AVCaptureDeviceInput。
>3.? 創建AVCaptureSession
>4.? 把創建的AVCaptureDeviceInput加入AVCaptureSession
>5.? 添加視頻預覽圖層AVCaptureVideoPreviewLayer
>6.? 創建AVCaptureAudioDataOutput,并加入AVCaptureSession
>7.? 啟動會話
官方步驟(可以忽略):
> 1. 創建AVCaptureSession對象
> 2. 獲取AVCaptureDevice錄像設備(攝像頭),錄音設備(麥克風)。只用于配置
> 3. 根據音頻/視頻硬件設備(AVCaptureDevice)創建音頻/視頻硬件輸入數據對象(AVCaptureDeviceInput),專門管理數據輸入。
> 4. 創建視頻輸出數據管理對象(AVCaptureVideoDataOutput),并且設置樣品緩存代理(setSampleBufferDelegate)就可以通過它拿到采集到的視頻數據
> 5. 創建音頻輸出數據管理對象(AVCaptureAudioDataOutput),并且設置樣品緩存代理(setSampleBufferDelegate)就可以通過它拿到采集到的音頻數據
> 6. 將數據輸入對象AVCaptureDeviceInput、數據輸出對象AVCaptureOutput添加到媒體會話管理對象AVCaptureSession中,就會自動讓音頻輸入與輸出和視頻輸入與輸出產生連接.
> 7. 創建視頻預覽圖層AVCaptureVideoPreviewLayer并指定媒體會話,添加圖層到顯示容器layer中
> 8. 啟動AVCaptureSession,只有開啟,才會開始輸入到輸出數據流傳輸
其中`AVCaptureAudioDataOutput`、`AVCaptureVideoDataOutput`包含兩個代理方法,可以一直監聽捕獲屬性。
```
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if (self.videoConnection == connection)
{
NSLog(@"采集到視頻");
}
else if(self.audioConnection == connection)
{
NSLog(@"采集到音頻");
}
}
// 丟失幀會調用這里
- (void)captureOutput:(AVCaptureOutput *)captureOutput didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection NS_AVAILABLE(10_7, 6_0)
{
NSLog(@"丟失幀");
}
```
不清楚可以看 : [官方文檔](https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/04_MediaCapture.html)
###2.3 捕捉圖片
`AVCaptureStillImageOutput`可以將捕獲到的Video轉換為圖片。
> 1. 創建device
> 2. 根據device創建deviceInput
> 3. 添加deviceInput進session
> 4. 添加預覽圖層
> 5. 添加deviceOutput進session
> 6. 調用AVCaptureConnection生成圖片
###2.4 捕捉視頻
`AVCaptureMovieFileOutput` 可以將捕捉到的視頻輸出到磁盤。可以設置錄制最長時限或錄制到特定大小,還可以配置成保留最小可用磁盤空間。
> 1. 創建device
> 2. 根據device創建deviceInput
> 3. 添加deviceInput進session
> 4. 添加預覽圖層
> 5. 添加deviceOutput進session
> 6. 調用AVCaptureMovieFileOutput把視頻寫入文件
`AVCaptureMovieFileOutput`包含有幾個代理方法。分別是`視頻開始錄制`,`視頻暫停`,`視頻恢復`, `視頻將要錄制完成`, `視頻錄制完成`。
#2.5 采集音頻視頻按幀輸出流程解析
> 1.找到物理設備攝像頭_inputCamera、麥克風_microphone,創建攝像頭輸入videoInput和麥克風輸入audioInput;
>
> 2.設置videoInput和audioInput為_captureSession的輸入,同時設置videoOutput和audioOutput為_captureSession的輸出,并且設置videoOutput和audioOutput的輸出delegate;
>
> 3._captureSession調用startRunning,開始捕獲信號;
>
> 4.音頻數據到達,把數據轉發給之前設置的audioEncodingTarget,并通過調用assetWriterAudioInput的appendSampleBuffer方法寫入音頻數據;
>
> 5.視頻數據到達,視頻數據傳入響應鏈,經過處理后通過assetWriterPixelBufferInput的appendSampleBuffer方法寫入視頻數據;
>
> 6.選擇保存后,文件通過ALAssertLibrary寫入手機照片庫。
流程圖:

###2.6 Demo在這里
代碼 : [捕獲音視頻Demo](https://github.com/tiantianlan/LiveExplanation/tree/master/%E6%8D%95%E8%8E%B7%E9%9F%B3%E8%A7%86%E5%B1%8F)
##3. GPUImage
`前面好像沒看懂,可以看這里嗎?`
可以,GPUImage對AVFundation進行了一層封裝,就算你不會前面的也沒關系。
###3.1 基本概念
`GPU` 手機或者電腦用于處理圖像渲染的硬件
`OpenGL ES` 一套圖形與硬件接口,用于把處理好的圖片顯示到屏幕上。
`GPUImage` 是一個基于OpenGL ES 2.0圖像和視頻處理的開源iOS框架,提供各種各樣的圖像處理濾鏡,并且支持照相機和攝像機的實時濾鏡,內置120多種濾鏡效果,并且能夠自定義圖像濾鏡。
`GPUImage` 是一個基于OpenGL ES 2.0圖像和視頻處理的開源iOS框架,提供各種各樣的圖像處理濾鏡,并且支持照相機和攝像機的實時濾鏡,內置120多種濾鏡效果,并且能夠自定義圖像濾鏡。
`濾鏡處理的原理` 就是把靜態圖片或者視頻的每一幀進行圖形變換再顯示出來。它的本質就是像素點的坐標和顏色變化
###3.1 利用GPUImage處理直播過程中美顏流程
`采集視頻 => 獲取每一幀圖片 => 濾鏡處理 => GPUImageView展示`

###3.2 處理畫面原理
GPUImage采用鏈式方式來處理畫面,通過addTarget:方法為鏈條添加每個環節的對象,處理完一個target,就會把上一個環節處理好的圖像數據傳遞下一個target去處理,稱為GPUImage處理鏈。
一般的target可以分為兩類:
`中間環節`的target,一般是指各種filter,是GPUImageFilter或者是子類
`最終環節`的target,GPUImageView 用于顯示到屏幕上或者GPUImageMovieWriter寫成視頻文件。
主要分為三個環節:
`source(視頻,圖片源) => filter(濾鏡) => final target(處理后的視頻、圖片)`
###3.3 美顏原理
`磨皮(GPUImageBilateralFilter)`:本質就是讓像素點模糊,可以使用高斯模糊,但是可能導致邊緣會不清晰,用雙邊濾波(Bilateral Filter) ,有針對性的模糊像素點,能保證邊緣不被模糊。
`美白(GPUImageBrightnessFilter)`:本質就是提高亮度。
###3.4 GPUImage源對象
GPUImage的數據源只能是4類:
`GPUImageVideoCamera` ios攝像頭的實時美顏。GPUImageVideoCamera是GPUImageOutput的子類,提供來自攝像頭的圖像數據作為源數據,一般是響應鏈的源頭。
`GPUImageStillCamera` 相機拍照
`GPUImagePicture` 處理靜止圖像
`GPUImageMovie` 電影
###3.5 用法
>1. 創建過濾器
>2. 創建源對象
>3. 把過濾器添加到源對象
>4. 生成target
靜態圖片處理:
```
UIImage *inputImage = [UIImage imageNamed:@"105"];
// 創建過濾器
GPUImageBrightnessFilter *filter = [[GPUImageBrightnessFilter alloc] init];
filter.brightness = 0.5;
[filter forceProcessingAtSize:inputImage.size];
[filter useNextFrameForImageCapture]; // 告訴系統從后來捕獲過濾器
// 處理靜止的圖像
GPUImagePicture *stillPic = [[GPUImagePicture alloc] initWithImage:inputImage];
[stillPic addTarget:filter]; //添加過濾器
[stillPic processImage]; // 執行渲染
UIImage *newImage = [filter imageFromCurrentFramebuffer];
UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage];
[imageView sizeToFit];
[self.view addSubview:imageView];
imageView.center = CGPointMake(CGRectGetWidth(self.view.frame)/2, CGRectGetHeight(self.view.frame)/2);
```
實時美顏處理:
```
// 創建視頻源
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetHigh cameraPosition:AVCaptureDevicePositionBack];
// 設置方向
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
// 創建預覽View
GPUImageView *videoPreview = [[GPUImageView alloc] initWithFrame:self.view.bounds];
[self.view insertSubview:videoPreview atIndex:0];
// 添加預覽圖層到源
GPUImageBeautifyFilter *fiter = [[GPUImageBeautifyFilter alloc] init];
[_videoCamera addTarget:fiter];
[fiter addTarget:self.videoPreview];
// 開始采集視頻
[videoCamera startCameraCapture];
```
到這里,僅僅是屏幕顯示的內容有濾鏡效果,而作為直播應用,還需要輸出帶有美顏效果的視頻流。
我們需要使用`GPUImageMovieWriter`類,才能處理視頻流。
核心思路:
通過GPUImageVideoCamera采集視頻和音頻信息,音頻信息直接發送給`GPUImageMovieWriter`,視頻信息傳入響應鏈作為源頭,渲染后的視頻再寫入`GPUImageMovieWriter`,同時通過`GPUImageView`顯示在屏幕上。只需要`addTarget`就可以添加`GPUImageMovieWriter`;

###3.6 實例代碼在這里
[美顏的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/GPUImage)
##4. 音視頻編碼,解碼
這一章太難了,以后再寫。
### VideoToolBox
### AudioToolBox
##5. 流媒體服務器
國內外有很多好用的流媒體服務區。這里為了方便搭建我們采用`nginx+RTMP`搭建流媒體服務器。
###5.1 MAC環境搭建
[MACOS上搭建nginx+rtmp環境](https://github.com/tiantianlan/LiveExplanation/blob/master/MACOS%E4%B8%8A%E6%90%AD%E5%BB%BAnginx%2Brtmp%E7%8E%AF%E5%A2%83.md)
###5.2 Centos環境搭建
[Centos下搭建的nginx + RTMP環境](https://github.com/tiantianlan/LiveExplanation/blob/master/Centos%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)
###5.3 服務端常用技術
`CDN` 直播數據上傳到服務器后,觀看直播的人比較多,服務器是承受不了的,會將數據分發到CDN,觀眾直接去CDN獲取數據。減少服務器的負載。
`負載均衡` 由多臺服務器組成一個服務器集群,每次請求的時候,會根據服務器負載選擇處理請求的服務器。
##6. 推流
###6.1 推流協議的選擇
[HLS和RTMP](https://github.com/tiantianlan/LiveExplanation/blob/master/%E7%9B%B4%E6%92%AD%E5%8D%8F%E8%AE%AE%E7%9A%84%E9%80%89%E6%8B%A9.md)
###6.2 推流原理
在iOS設備上進行各推流的話,是通過AVCaptureSession這么一個捕捉會話,指定兩個AVCaptureDevice 也就是iOS的攝像頭和麥克風,獲取個原始視頻和音頻,然后需要進行個H.264的視頻編碼和AAC的音頻編碼,再將編碼后的數據整合成一個音視頻包,通過rmtp推送到nginx服務器
###6.3 librtmp
這個參考資料很少。不過大部分都采用的這個。
因為涉及太多C/C++這里不討論。
參考:[https://my.oschina.net/jerikc/blog/501948](https://my.oschina.net/jerikc/blog/501948)
##7. 音視頻播放
###7.1 播放框架的選擇
iOS的播放框架主要有以下三種:
>1. AVPlayer 可以播放本地、遠程視頻,可以自定義UI進行控制
>2. AVPlayerViewController 自帶播放控制UI,不能自定義UI
>3. MPMoviePlayerController,MPMoviePlayerViewController (iOS9后棄用)
如果只是簡單的播放視頻,選擇`AVPlayerViewController`,如果想自定義播放器,選擇`AVPlayer`。
###7.2 AVPlayer
AVPlayer是一個用來播放基于時間的流媒體控制對象。支持播放從本地、分布下載或通過HTTP Live Streaming協議得到的流媒體。
AVPlayer只管理一個單獨資源的播放,不過框架還提供了AVPlayer的一個子類AVQueuePlayer,可以用來管理一個資源隊列。當你需要在一個序列中播放多個條目或者為音頻、視頻資源設置播放循環時可以使用該子類。
AVPlayer視頻播放使用步驟:
>1. 創建視頻資源地址URL,可以是網絡URL
>2. 通過URL創建視頻內容對象`AVPlayerItem`,一個視頻對應一個`AVPlayerItem`
>3. 創建`AVPlayer`視頻播放對象,需要一個`AVPlayerItem`進行初始化
>4. 創建`AVPlayerLayer`播放圖層對象,添加到現實視圖上去
>5. 添加KVO監聽。 監聽到AVPlayerItemStatusReadyToPlay的時候調用play方法
[AVPlayer的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/AVPlayer)
###7.3 AVPlayerViewController
`AVPlayerViewController`屬于`AV Kit`,它是`UIViewController的子類`,用于展示并控制AVPlayer實例的播放。
`AVPlayerViewController `類使用步驟
>1. 創建URL
>2. 創建AVPlayerViewController,并根據URL設置player屬性
>3. 調用play方法
[AVPlayerViewController的使用](https://github.com/tiantianlan/LiveExplanation/tree/master/AVKit)
Xcode8模擬器可能有問題,打開播放不了。
###8.開源框架
前面所講都有第三方框架支持。采集、美顏、推流有`LFLiveKit`, 拉流播放有`IJKMediaFramework`。
[`LFLiveKit`](https://github.com/LaiFengiOS/LFLiveKit) : LFLiveKit是iOS版開源RTMP流SDK。他支持后臺錄制、美顏功能、支持h264、AAC硬編碼,動態改變速率,RTMP傳輸等
[`IJKMediaFramework`](https://github.com/Bilibili/ijkplayer) : ijkplayer是B站開源的一款視頻直播框架,它是基于ffmpeg。 如果從github下載是需要編譯。參考地址:[http://www.lxweimin.com/p/1f06b27b3ac0](http://www.lxweimin.com/p/1f06b27b3ac0)
個人實驗只需要配置Nginx+RTMP服務
這里我采用這兩個第三方框架寫了一個直播,包含在線觀看直播,和直播推流,支持在線美顏,前后攝像頭切換等你需要:
>1. 搭建`Nginx+RTMP環境`:[MAC](https://github.com/tiantianlan/LiveExplanation/blob/master/MACOS%E4%B8%8A%E6%90%AD%E5%BB%BAnginx%2Brtmp%E7%8E%AF%E5%A2%83.md) 或者? [Centos](https://github.com/tiantianlan/LiveExplanation/blob/master/Centos%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA.md)
>2. 下載項目真機運行。[https://github.com/tiantianlan/miaoboDemo](https://github.com/tiantianlan/miaoboDemo)
登陸界面

主頁

直播頁面

參考文章:
>1. [知乎 如何搭建一個完整的直播系統](https://www.zhihu.com/question/42162310)
>2. [袁崢技術博客](http://www.lxweimin.com/users/b09c3959ab3b/latest_articles)
>3. [GPUImage 基于GPU加速的圖形和視頻處理庫](http://www.ios122.com/2015/08/gpuimage/)
>
>4. [iOS中為直播APP集成美顏功能](http://news.h5.com.cn/ios/49696.html)
>
>5. [iOS視頻直播~推流、拉流原理](http://www.lxweimin.com/p/e516a5b31480)
>
>6. [關于視頻直播技術,你想要知道的都在這里了](http://www.lxweimin.com/p/ddb640ac4fec)
>
>7. [macOS上搭建Nginx+rtmp服務器](https://www.xsd.me/mac_nginx_rtmp_server.html)