直播原理
:把主播錄製的視頻,推送到服務器,在由服務器分發給觀衆觀看。html
直播環節
:推流端(採集、美顏處理、編碼、推流)、服務端處理(轉碼、錄製、截圖、鑑黃)、播放器(拉流、解碼、渲染)、互動系統(聊天室、禮物系統、贊)前端
1.採集、2.濾鏡處理、3.編碼、4.推流、5.CDN分發、6.拉流、7.解碼、8.播放、9.聊天互動
ios
* 1.1 採集視頻、音頻編碼框架 *nginx
AVFoundation
:AVFoundation是用來播放和建立實時的視聽媒體數據的框架,同時提供Objective-C接口來操做這些視聽數據,好比編輯,旋轉,重編碼* 1.2 視頻、音頻硬件設備 *git
CCD
:圖像傳感器: 用於圖像採集和處理的過程,把圖像轉換成電信號。拾音器
:聲音傳感器: 用於聲音採集和處理的過程,把聲音轉換成電信號。音頻採樣數據
:通常都是PCM格式視頻採樣數據
: 通常都是YUV
,或RGB
格式,採集到的原始音視頻的體積是很是大的,須要通過壓縮技術處理來提升傳輸效率視頻處理原理
:由於視頻最終也是經過GPU,一幀一幀渲染到屏幕上的,因此咱們能夠利用OpenGL ES,對視頻幀進行各類加工,從而視頻各類不一樣的效果,就好像一個水龍頭流出的水,通過若干節管道,而後流向不一樣的目標
GPUImage
這個框架實現的,.* 視頻處理框架 *github
GPUImage
: GPUImage是一個基於OpenGL ES的一個強大的圖像/視頻處理框架,封裝好了各類濾鏡同時也能夠編寫自定義的濾鏡,其自己內置了多達120多種常見的濾鏡效果。OpenGL
:OpenGL(全寫Open Graphics Library)是個定義了一個跨編程語言、跨平臺的編程接口的規格,它用於三維圖象(二維的亦可)。OpenGL是個專業的圖形程序接口,是一個功能強大,調用方便的底層圖形庫。OpenGL ES
:OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL三維圖形 API 的子集,針對手機、PDA和遊戲主機等嵌入式設備而設計。* 3.1 視頻編碼框架 *web
FFmpeg
:是一個跨平臺的開源視頻框架,能實現如視頻編碼,解碼,轉碼,串流,播放等豐富的功能。其支持的視頻格式以及播放協議很是豐富,幾乎包含了全部音視頻編解碼、封裝格式以及播放協議。
X264
:把視頻原數據YUV編碼壓縮成H.264格式VideoToolbox
:蘋果自帶的視頻硬解碼和硬編碼API,可是在iOS8以後纔開放。AudioToolbox
:蘋果自帶的音頻硬解碼和硬編碼API* 3.2 視頻編碼技術 *redis
視頻壓縮編碼標準
:對視頻進行壓縮(視頻編碼)或者解壓縮(視頻解碼)的編碼技術
,好比MPEG
,H.264
,這些視頻編碼技術是壓縮編碼視頻的
主要做用
:是將視頻像素數據壓縮成爲視頻碼流,從而下降視頻的數據量。若是視頻不通過壓縮編碼的話,體積一般是很是大的,一部電影可能就要上百G的空間。注意
:最影響視頻質量的是其視頻編碼數據和音頻編碼數據,跟封裝格式沒有多大關係MPEG
:一種視頻壓縮方式,它採用了幀間壓縮,僅存儲連續幀之間有差異的地方 ,從而達到較大的壓縮比H.264/AVC
:一種視頻壓縮方式,採用事先預測和與MPEG中的P-B幀同樣的幀預測方法壓縮,它能夠根據須要產生適合網絡狀況傳輸的視頻流,還有更高的壓縮比,有更好的圖象質量
注意1
:若是是從單個畫面清晰度比較,MPEG4有優點;從動做連貫性上的清晰度,H.264有優點注意2
:因爲264的算法更加複雜,程序實現煩瑣,運行它須要更多的處理器和內存資源。所以,運行264對系統要求是比較高的。注意3
:因爲264的實現更加靈活,它把一些實現留給了廠商本身去實現,雖然這樣給實現帶來了不少好處,可是不一樣產品之間互通成了很大的問題,形成了經過A公司的編碼器編出的數據,必須經過A公司的解碼器去解這樣尷尬的事情H.265/HEVC
:一種視頻壓縮方式,基於H.264,保留原來的某些技術,同時對一些相關的技術加以改進,以改善碼流、編碼質量、延時和算法複雜度之間的關係,達到最優化設置。
I幀
:(關鍵幀)保留一副完整的畫面,解碼時只須要本幀數據就能夠完成(由於包含完整畫面)P幀
:(差異幀)保留這一幀跟以前幀的差異,解碼時須要用以前緩存的畫面疊加上本幀定義的差異,生成最終畫面。(P幀沒有完整畫面數據,只有與前一幀的畫面差異的數據)B幀
:(雙向差異幀)保留的是本幀與先後幀的差異,解碼B幀,不只要取得以前的緩存畫面,還要解碼以後的畫面,經過先後畫面的與本幀數據的疊加取得最終的畫面。B幀壓縮率高,可是解碼時CPU會比較累幀內(Intraframe)壓縮
:當壓縮一幀圖像時,僅考慮本幀的數據而不考慮相鄰幀之間的冗餘信息,幀內通常採用有損壓縮算法幀間(Interframe)壓縮
:時間壓縮(Temporal compression),它經過比較時間軸上不一樣幀之間的數據進行壓縮。幀間壓縮通常是無損的muxing(合成)
:將視頻流、音頻流甚至是字幕流封裝到一個文件中(容器格式(FLV,TS)
),做爲一個信號進行傳輸。* 3.3 音頻編碼技術 *算法
AAC
,mp3
:這些屬於音頻編碼技術,壓縮音頻用* 3.4碼率控制 *編程
多碼率
:觀衆所處的網絡狀況是很是複雜的,有多是WiFi,有可能4G、3G、甚至2G,那麼怎麼知足多方需求呢?多搞幾條線路,根據當前網絡環境自定義碼率。
* 3.5 視頻封裝格式 *
TS
: 一種流媒體封裝格式,流媒體封裝有一個好處,就是不須要加載索引再播放,大大減小了首次載入的延遲,若是片子比較長,mp4文件的索引至關大,影響用戶體驗
爲何要用TS
:這是由於兩個TS片斷能夠無縫拼接,播放器能連續播放FLV
: 一種流媒體封裝格式,因爲它造成的文件極小、加載速度極快,使得網絡觀看視頻文件成爲可能,所以FLV格式成爲了當今主流視頻格式
* 4.1 數據傳輸框架 *
librtmp
:用來傳輸RTMP協議格式的數據
* 4.2 流媒體數據傳輸協議 *
RTMP
:實時消息傳輸協議,Adobe Systems公司爲Flash播放器和服務器之間音頻、視頻和數據傳輸開發的開放協議,由於是開放協議因此均可以使用了。
chunk
:消息包
* 5.1經常使用服務器 *
SRS
:一款國人開發的優秀開源流媒體服務器系統BMS
:也是一款流媒體服務器系統,但不開源,是SRS的商業版,比SRS功能更多nginx
:免費開源web服務器,經常使用來配置流媒體服務器。* 5.2數據分發 *
CDN
:(Content Delivery Network),即內容分發網絡,將網站的內容發佈到最接近用戶的網絡」邊緣」,使用戶能夠就近取得所需的內容,解決 Internet網絡擁擠的情況,提升用戶訪問網站的響應速度.
CDN
:代理服務器,至關於一箇中介。CDN工做原理
:好比請求流媒體數據
回源
:當有用戶訪問某一個URL的時候,若是被解析到的那個CDN節點沒有緩存響應的內容,或者是緩存已經到期,就會回源站
去獲取搜索。若是沒有人訪問,那麼CDN節點不會主動去源站
拿.帶寬
:在固定的時間可傳輸的數據總量,
負載均衡
: 由多臺服務器以對稱的方式組成一個服務器集合,每臺服務器都具備等價的地位,均可以單獨對外提供服務而無須其餘服務器的輔助.
QoS(帶寬管理)
:限制每個組羣的帶寬,讓有限的帶寬發揮最大的效用拉流,就是從流媒體服務器獲取音頻,視頻數據
直播協議選擇
:
RTMP
,RTSP
HLS
直播協議對比
:HLS
:由Apple公司定義的用於實時流傳輸的協議,HLS基於HTTP協議實現,傳輸內容包括兩部分,一是M3U8描述文件,二是TS媒體文件。可實現流媒體的直播和點播,主要應用在iOS系統
以點播的技術方式
來實現直播自適應碼率流播
,客戶端會根據網絡情況自動選擇不一樣碼率的視頻流,條件容許的狀況下使用高碼率,網絡繁忙的時候使用低碼率,而且自動在兩者間隨意切HLS與RTMP對比
:HLS主要是延時比較大,RTMP主要優點在於延時低
HTTP-FLV
:基於HTTP協議流式的傳輸媒體內容。
RTSP
:實時流傳輸協議,定義了一對多應用程序如何有效地經過IP網絡傳送多媒體數據.RTP
:實時傳輸協議,RTP是創建在UDP協議上的,常與RTCP一塊兒使用,其自己並無提供按時發送機制或其它服務質量(QoS)保證,它依賴於低層服務去實現這一過程。RTCP
:RTP的配套協議,主要功能是爲RTP所提供的服務質量(QoS)提供反饋,收集相關媒體鏈接的統計信息,例如傳輸字節數,傳輸分組數,丟失分組數,單向和雙向網絡延遲等等。* 7.1 解封裝 *
demuxing(分離)
:從視頻流、音頻流,字幕流合成的文件(容器格式(FLV,TS)
)中, 分解出視頻、音頻或字幕,各自進行解碼。* 7.2 音頻編碼框架 *
fdk_aac
:音頻編碼解碼框架,PCM音頻數據和AAC音頻數據互轉* 7.3 解碼介紹 *
硬解碼
:用GPU來解碼,減小CPU運算
軟解碼
:用CPU來解碼
流媒體開發
:網絡層(socket或st)負責傳輸,協議層(rtmp或hls)負責網絡打包,封裝層(flv、ts)負責編解碼數據的封裝,編碼層(h.264和aac)負責圖像,音頻壓縮。
幀
:每幀表明一幅靜止的圖像GOP
:(Group of Pictures)畫面組,一個GOP就是一組連續的畫面,每一個畫面都是一幀,一個GOP就是不少幀的集合
碼率
:圖片進行壓縮後每秒顯示的數據量。幀率
:每秒顯示的圖片數。影響畫面流暢度,與畫面流暢度成正比:幀率越大,畫面越流暢;幀率越小,畫面越有跳動感。
分辨率
:(矩形)圖片的長度和寬度,即圖片的尺寸壓縮前的每秒數據量
:幀率X分辨率(單位應該是若干個字節)壓縮比
:壓縮前的每秒數據量/碼率 (對於同一個視頻源並採用同一種視頻編碼算法,則:壓縮比越高,畫面質量越差。) 視頻文件格式
:文件的後綴
,好比.wmv,.mov,.mp4,.mp3,.avi
,
主要用處
,根據文件格式,系統會自動判斷用什麼軟件打開,視頻封裝格式
:一種儲存視頻信息的容器
,流式封裝能夠有TS、FLV
等,索引式的封裝有MP4,MOV,AVI
等,
主要做用
:一個視頻文件每每會包含圖像和音頻,還有一些配置信息(如圖像和音頻的關聯,如何解碼它們等):這些內容須要按照必定的規則組織、封裝起來.注意
:會發現封裝格式跟文件格式同樣,由於通常視頻文件格式的後綴名即採用相應的視頻封裝格式的名稱,因此視頻文件格式就是視頻封裝格式。視頻封裝格式和視頻壓縮編碼標準
:就好像項目工程和編程語言,封裝格式就是一個項目的工程,視頻編碼方式就是編程語言,一個項目工程能夠用不一樣語言開發。
框架 :IJKPlayer
直播中最最重要的元素,那就是視頻播放器了。視頻播放器的選擇,實際上是很是多的,最著名的,非b站的IJKPlayer莫屬了。其實如今不少三方的播放器,開發者在編寫的時候,都是按照系統的MPMoviePlayer的接口設計的,因此,你只要學會使用一個播放器,其餘播放器的使用都會很輕鬆的上手。
附上兩個比較有參考價值的demo,一個是自定義IJKPlayer,進度條、音量、亮度,能夠參考這個demo,對IJKPlayer進行深度的個性化定製(其餘播放器也適用),好比大部分播放器支持的屏幕手勢(滑動調整音量、亮度、進度等),另外一個是ZFPlayer,這個播放器基於AVPlayer,主要能夠參考裏面的橫豎平切換的處理,也能夠直接拿來作普通的視頻播放器繼承在應用中,不少功能都已經作好,用起來很方便。
首先你要找到一個測試服務器或者建立本地Nginx服務器,搭建本地服務器請看Mac搭建nginx+rtmp服務器
框架:LFLiveKit
關於直播的推流,目前來講最火的應該是這個了LFLiveKit。
我的仿寫項目
但有一些我的仿寫項目都是IJKPlayer配合LFLiveKit完成的。
下面推薦幾個我的仿寫的項目,能夠參考下大部分直播中會出現的場景的處理策略。
這個是仿映客的520Linkee,這個是仿喵播的MiaowShow,(能夠看它們相關的博客講解)
這兩個都是市面上比較常見的我的手機端直播的典型實現方案。
本身實現:模擬實現視頻聊天功能
推流端用的是LFLiveKit框架,拉流用IJKPlayer,服務端找到一個測試服務器或者建立本地Nginx服務器
相關圖:
一、聊天
二、禮物
三、直播列表
四、本身直播
五、房間邏輯
六、用戶邏輯
七、觀看直播
八、統計
九、超管
SDK:金山雲、七牛雲、阿里雲、網易雲、騰訊雲
金山雲的SDK:他們的SDK更新頻率挺快的,並且最新版已經支持https了。但他們的SDK也存在一些bug,不過好在他們的每一版更新都會及時的進行修復。
通過對比了好多家的SDK demo(阿里、網易、騰訊、七牛等)後,你會發現金山的SDK demo是寫的最完善的,推流端你直接拿過來給個推流地址就能夠推了,包括美顏、碼率、編碼等等,都在demo上有選項可供設置,你只要在開發的時候,對這些功能從新設計下UI就行了。播放器demo、推流demo,建議在使用的過程當中,多跟進他們的更新release,你會發現他們每次更新都會優化不少功能、修復不少bug
SDK:融雲、環信和野狗
既然你們都在看直播,互動確定也少不了,直播聊天室就必需要有。咱們用的是融雲,由於融雲的宣傳和口碑都不錯,因此就選擇了融雲,並且也是好多直播服務商的合做夥伴,因此能夠放心使用。其餘的還有環信和野狗,環信的控制檯和文檔,不如融雲友好,野狗的沒有試過,我的建議使用融雲。並且融雲官網有集成了播放器、聊天的直播間demo能夠參考,裏面帶了一個香港某電視臺的直播流,能夠用來測試用來 rtmp://live.hkstv.hk.lxdns.com/live/hks 。
而後聊天中的聊天列表的處理,能夠參考個人這篇簡書來處理,以優化性能 http://www.jianshu.com/p/518e9c169274 。
這裏有一點須要注意,在一個controller中,將當前controller設置爲融雲的消息接收代理,就能夠接收融雲消息了。
[[RCIMClient sharedRCIMClient] setReceiveMessageDelegate:self object:nil];
在頁面dealloc中不要只調用 [RCIMClient sharedRCIMClient] quitChatRoom 退出直播間就以爲沒事了,由於退出直播間是異步的,可能在當前controller dealloc後纔會退出,若是在這段時間收到新的消息,[RCIMClient sharedRCIMClient]就會由於delegate釋放了而致使崩潰,因此要在當前controller的dealloc中設置消息接收代理爲nil。
[[RCIMClient sharedRCIMClient] setReceiveMessageDelegate:nil object:nil];
點贊動畫能夠參考這個 https://github.com/singer1026/DMHeartFlyAnimation ,主要經過CAKeyFrameAnimation和UIBezierPath完成,也能夠自行修改代碼修改動畫軌跡、替換點贊圖片等。
彈幕建議使用BarrageRenderer,性能不錯,git主頁的介紹,就能讓你很簡單的上手使用,但若是你要作歷史消息的彈幕和即時消息結合的彈幕,建議歷史彈幕的遍歷以及時間軸綁定,仍是本身寫比較好,由於這個庫的redisplay以及綁定時間軸方法,在與即時消息結合的時候,彈幕的展現可能會有重複出現屢次的現象。
直播中咱們要考慮用戶的當前網絡狀態,移動網絡幫他中止播放,或者切換到wifi的時候,幫他重連,以減小流量的耗費。網絡的變化主要經過兩種方式判斷,一種是Reachability,另外一種是獲取狀態欄上的網絡狀態。
Reachability寫在AppDelegate中,在網絡狀態變化的時候,block中的代碼就會被調用,你想把網絡變化的消息發送給直播頁面,直接用通知中心就能夠,而後Reachability建議使用AFNetworking的,由於以前有文章說Reachability庫可能會引發不支持ipv6致使審覈被拒,咱們項目中用的AFNetworking中的Reachability,沒有問題:
- (void)monitorNetworking { AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager]; [mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { switch (status) { wifi網絡 break; 移動網絡 break; case AFNetworkReachabilityStatusNotReachable: 無網絡 break; case AFNetworkReachabilityStatusUnknown: 未知網絡 break; default: break; } }]; //開始監控 [mgr startMonitoring]; }
獲取狀態欄網絡狀態,有人說在狀態欄隱藏的頁面,無法獲取網絡狀態,實測是能夠獲取的,方法裏面有我寫的枚舉,替換下就行了:
- (NSString *)getCurrentNetWork { NSArray *subviews = [[[[UIApplication sharedApplication] valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews]; for (id child in subviews) { if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) { //獲取到狀態欄碼 int networkType = [[child valueForKeyPath:@"dataNetworkType"] intValue]; switch (networkType) { case 0: { // states = NetworkStatesNone; return CurrentNetWorkNone; } break; case 1: { // states = NetworkStates2G; return CurrentNetWorkMobile; } break; case 2: { // states = NetworkStates3G; return CurrentNetWorkMobile; } break; case 3: { // states = NetworkStates4G; return CurrentNetWorkMobile; } break; case 5: { // states = NetworkStatesWIFI; return CurrentNetWorkWifi; } break; default: { return CurrentNetWorkNone; } break; } } } return CurrentNetWorkNone; }