iOS直播技術總結
直播總結
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技術點
下面我們會選擇一部分技術進行講解。
2. 視頻采集
2.1 基本知識介紹
AVFundation : 音視頻數據采集需要用AVFundation框架
AVCaptureDevice : 硬件設備,包括麥克風、攝像頭、通過該對象可以設置物理設備的一些屬性。例如相機焦距,白平衡等
AVCaptureDeviceInput : 硬件輸入對象,可以根據AVCaptureDevice創建對應的AVCaptureDeviceInput對象,用于管理硬件輸入數據
AVCaptureOutput : 硬件輸出對象,用于接收各類輸出數據,通常使用對應的子類AVCaptureAudioDataOutput(聲音數據輸出對象), AVCaptureVideoDataOutput(視頻輸出對象)
AVCaptureConnection : 當把一個輸入和輸出添加到AVCaptureSession后。AVCaptureSession就會在輸出、輸出設備之間建立連接,而且通過AVCaptureOutput可以獲得這個對象
AVCaptureVideoPreviewLayer : 相機拍攝預覽圖層,能實時查看相機效果。創建該對象需要指定對應的AVCaptureSession對象,因為AVCaptureSession包含輸出數據,有視頻數據才能顯示。
AVCaptureSession : 協調輸入與輸出之間傳遞數據
2.2 捕獲音視頻步驟
包含關系:
步驟:
- 創建AVCaptureDevice(video或者audio)
- 根據AVCaptureDevice創建AVCaptureDeviceInput。
- 創建AVCaptureSession
- 把創建的AVCaptureDeviceInput加入AVCaptureSession
- 添加視頻預覽圖層AVCaptureVideoPreviewLayer
- 創建AVCaptureAudioDataOutput,并加入AVCaptureSession
- 啟動會話
官方步驟(可以忽略):
- 創建AVCaptureSession對象
- 獲取AVCaptureDevice錄像設備(攝像頭),錄音設備(麥克風)。只用于配置
- 根據音頻/視頻硬件設備(AVCaptureDevice)創建音頻/視頻硬件輸入數據對象(AVCaptureDeviceInput),專門管理數據輸入。
- 創建視頻輸出數據管理對象(AVCaptureVideoDataOutput),并且設置樣品緩存代理(setSampleBufferDelegate)就可以通過它拿到采集到的視頻數據
- 創建音頻輸出數據管理對象(AVCaptureAudioDataOutput),并且設置樣品緩存代理(setSampleBufferDelegate)就可以通過它拿到采集到的音頻數據
- 將數據輸入對象AVCaptureDeviceInput、數據輸出對象AVCaptureOutput添加到媒體會話管理對象AVCaptureSession中,就會自動讓音頻輸入與輸出和視頻輸入與輸出產生連接.
- 創建視頻預覽圖層AVCaptureVideoPreviewLayer并指定媒體會話,添加圖層到顯示容器layer中
- 啟動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(@"丟失幀");
}</code></pre>
2.3 捕捉圖片
AVCaptureStillImageOutput 可以將捕獲到的Video轉換為圖片。
- 創建device
- 根據device創建deviceInput
- 添加deviceInput進session
- 添加預覽圖層
- 添加deviceOutput進session
- 調用AVCaptureConnection生成圖片
2.4 捕捉視頻
AVCaptureMovieFileOutput 可以將捕捉到的視頻輸出到磁盤。可以設置錄制最長時限或錄制到特定大小,還可以配置成保留最小可用磁盤空間。
- 創建device
- 根據device創建deviceInput
- 添加deviceInput進session
- 添加預覽圖層
- 添加deviceOutput進session
- 調用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
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 用法
- 創建過濾器
- 創建源對象
- 把過濾器添加到源對象
- 生成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);</code></pre>
實時美顏處理:
// 創建視頻源
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];</code></pre>
到這里,僅僅是屏幕顯示的內容有濾鏡效果,而作為直播應用,還需要輸出帶有美顏效果的視頻流。 我們需要使用 GPUImageMovieWriter 類,才能處理視頻流。
核心思路:
通過GPUImageVideoCamera采集視頻和音頻信息,音頻信息直接發送給 GPUImageMovieWriter ,視頻信息傳入響應鏈作為源頭,渲染后的視頻再寫入 GPUImageMovieWriter ,同時通過 GPUImageView 顯示在屏幕上。只需要 addTarget 就可以添加 GPUImageMovieWriter ;

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

主頁

直播頁面

來自:https://github.com/tiantianlan/LiveExplanation