本文來自於騰訊bugly開發者社區,非經做者贊成,請勿轉載,原文地址:http://dev.qq.com/topic/5811d42e7fd6ec467453bf58javascript
做者:李智文html
分享內容:前端
互聯網內容載體變遷歷程,文字——圖片/聲音——視頻——VR/AR——…….。從直播1.0秀場時代(YY),2.0遊戲直播(鬥魚、虎牙、熊貓)到現在全民直播3.0泛生活娛樂時代(映客、花椒),國外直播app(Meerkat 、Periscope),隨着VA/AR/MR提出的沉浸式視聽體驗,直播4.0時代很快就能到來。java
在這個全民娛樂的時代,直播已經火得不要不要的,各大公司都有本身的直播產品。本文主要從直播的一些基本知識,一步步打造直播app。直播那麼火的背後有什麼樣的技術支撐呢?android
先將這些APP按照視頻網站按照視頻網站、彈幕視頻、直播平臺、在線秀場、移動短視頻、移動直播來劃分類別。再按照內容和社交這個維度來進行區分,能夠明顯看出視頻網站、彈幕網站和直播平臺更偏內容,他們對內容的需求更加高,用戶在上面進行社交沉澱相對比較淺。ios
然後面三者更加偏向社交,他們強調人而不強調內容。因此短時間內不會有大的競爭關係,只是前三類、後三者之間的競爭會出現。nginx
大致框架 基本是下圖這個套路:git
錄製->編碼->網絡傳輸->解碼->播放github
以上爲直播的總體流程,根據該流程分爲如下技術點:web
PC直播(固定場所)——>移動端(形式自由)。
隨着愈來愈多的直播類 App 上線,移動直播進入了史無前例的爆發階段,目前大多數移動直播以 Native 客戶端爲主。可是H5端的直播在移動直播端也承載着不可替代的做用,例如 H5 有着傳播快,易發佈的優點。
完整的直播包括:
如何生產視頻數據
封裝格式的主要做用是把視頻碼流和音頻碼流按照必定的格式存儲在一個文件中。
爲何要分封裝格式和視頻編碼格式呢? 這個其實跟網絡分七層模型一個原理。解耦和,下降依賴,底層給上層提供基礎功能,底層和上層都均可以單獨擴展,能夠以多種方案組合編碼與封裝,好比MP4與H26四、MP4與MPEG、TS與H264等等。好比這裏面的這邊文章的編碼就只負責將最原始的音頻和視頻數據就行壓縮,而壓縮完的數據要怎麼組織就拜託給上層的封裝,封裝接到視頻音頻數據負責給數據編號,指定同步協議,加入字幕等操做。通過封裝後,獲得的就是能夠播放的上面提到的視頻文件MP4或者MKV等等。把這個過程反過來就是上圖描述的視頻播放的過程。
PC端的攝像頭、屏幕 對於PC端的流媒體源,可使用Open Broadcaster Software串流(支持多種直播平臺)。
移動端iOS、Android的攝像頭和麥克風。 iOS、Android主要是系統提供的API實現。
webRTC (Web Real-Time Communication) webRTC是一個支持網頁瀏覽器進行實時語音對話或視頻對話的技術,能夠在網頁瀏覽器中進行採集、傳輸、播放,缺點是隻在 PC 的 Chrome 上支持較好,移動端支持不太理想。
使用 webRTC 錄製視頻基本流程是:
window.navigator.webkitGetUserMedia()
獲取用戶的PC攝像頭視頻數據。window.webkitRTCPeerConnection
(一種視頻流數據格式)。webscoket
將視頻流數據傳輸到服務端因爲許多方法都要加上瀏覽器前綴,因此不少移動端的瀏覽器還不支持 webRTC,因此真正的視頻錄製仍是要靠客戶端(iOS,Android)來實現,效果會好一些。
推薦Andorid4.3(API18)或以上使用硬編,如下版本使用軟編;iOS使用全硬編方案。
FLV(Flash Video)是Adobe公司設計開發的一種流行的流媒體格式,FLV可使用Flash Player進行播放,FLV封裝格式的文件後綴一般爲「.flv」。整體上看,FLV包括文件頭(File Header)和文件體(File Body)兩部分,其中文件體由一系列的Tag組成。
特色:視頻文件體積輕巧、封裝簡單
每一個Tag前面還包含了Previous Tag Size字段,表示前面一個Tag的大小。Tag的類型能夠是視頻、音頻和Script,每一個Tag只能包含以上三種類型的數據中的一種。圖2展現了FLV文件的詳細結構。
Tag Data
Audio Tag
Video Tag
Script Tag(控制幀)或叫meta data tag 該類型Tag又一般被稱爲Metadata Tag,會放一些關於FLV視頻和音頻的元數據信息如:duration、width、height等。一般該類型Tag會跟在File Header後面做爲第一個Tag出現,並且只有一個。
如圖以Android爲例的推流的流程圖:
如何推 往哪裏推
國內常見公開的直播協議有幾個:RTMP、HDL(HTTP-FLV)、HLS、RTP。
Real Time Messaging Protocol是 Macromedia 開發的一套視頻直播協議,如今屬於 Adobe。
使用RTMP技術的流媒體系統有一個很是明顯的特色:使用 Flash Player 做爲播放器客戶端,而Flash Player 如今已經安裝在了全世界將近99%的PC上,所以通常狀況下收看RTMP流媒體系統的視音頻是不須要安裝插件的。用戶只須要打開網頁,就能夠直接收看流媒體。
和 HLS 同樣均可以應用於視頻直播,區別是 RTMP 基於 flash 沒法在 iOS 的瀏覽器裏播放,可是實時性比 HLS 要好。因此通常使用這種協議來上傳視頻流,也就是視頻流推送到服務器。
rtmp如今大部分國外的CDN已不支持,在國內流行度很高。緣由有幾個方面:
即便用HTTP協議流式的傳輸媒體內容,直接向後臺上傳編碼後的流媒體數據。相對於RTMP,HTTP更簡單和廣爲人知,並且不擔憂被Adobe的專利綁架。內容延遲一樣能夠作到2~5秒,打開速度更快,由於HTTP自己沒有複雜的狀態交互。因此從延遲角度來看,HTTP-FLV要優於RTMP。
SRS2.0支持該協議:GitHub
即Http Live Streaming,是由蘋果提出基於HTTP的流媒體傳輸協議。HLS有一個很是大的優勢:HTML5能夠直接打開播放;這個意味着能夠把一個直播連接經過微信等轉發分享,不須要安裝任何獨立的APP,有瀏覽器便可,因此流行度很高。社交直播APP,HLS能夠說是剛需 。
Issue:SRS3.0提出了一種加強型HLS+
Real-time Transport Protocol,用於Internet上針對多媒體數據流的一種傳輸層協議。
實際應用場景下常常須要RTCP(RTP Control Protocol)配合來使用,能夠簡單理解爲RTCP傳輸交互控制的信令,RTP傳輸實際的媒體數據。 RTP在視頻監控、視頻會議、IP電話上有普遍的應用,由於視頻會議、IP電話的一個重要的使用體驗:內容實時性強。
對比與上述3種或實際是2種協議,RTP和它們有一個重要的區別就是默認是使用UDP協議來傳輸數據,而RTMP和HTTP-FLV是基於TCP協議傳輸。
實時音視頻流的場景不須要可靠保障,所以也不須要有重傳的機制,實時的看到圖像聲音,網絡抖動時丟了一些內容,畫面模糊和花屏,徹底不重要。TCP爲了重傳會形成延遲與不一樣步,如某一截內容由於重傳,致使1秒之後纔到,那麼整個對話就延遲了1秒,隨着網絡抖動,延遲還會增長成2秒、3秒,若是客戶端播放是不加以處理將嚴重影響直播的體驗。
是否有除了HLS外更低延遲的方案?
HLS的優勢點是顯而易見的:移動端無需安裝APP使用兼容HTML5的瀏覽器打開便可觀看,全部主流的移動端瀏覽器基本都支持HTML5,在直播的傳播和體驗上有巨大的優點。
下面是 HTTP-FLV、HLS 、 RTMP 的對比:
所謂推流,就是將咱們已經編碼好的音視頻數據發往視頻流服務器中,經常使用的第三方庫 librtmp-iOS 進行推流,librtmp 封裝了一些核心的 API 供使用者調用。例如推流 API 等等,配置服務器地址,便可將轉碼後的視頻流推往服務器。通常的推流服務器都配置了服務器端信息。
百度雲推流SDK: 官方文檔 七牛推流SDK: Github上的官方源碼及說明 網易雲推流SDK:官方文檔 騰訊雲推流SDK:官方文檔 其餘推流SDK: https://github.com/daniulive/SmarterStreaming https://github.com/leixiaohua1020/simplest_ffmpeg_mobile https://github.com/begeekmyfriend/yasea https://github.com/simple-rtmp-server/srs-sea
那麼如何搭建一個推流服務器呢?
簡單的推流服務器搭建,服務器支持 RTMP ,大概須要如下幾個步驟:
下面是 nginx 的配置文件
[後臺SDK]主要是調用騰訊雲API。
[服務器API]提供了直播控制檯api概覽:
騰訊雲直播方案總體流程
方案根據騰訊雲的快速對接,最終造成閉環邏輯。
如何看
下載直播視頻有如下方式:
好看的指標參數 碼率:影響體積,與體積成正比:碼率越大,體積越大;碼率越小,體積越小。 幀率:影響畫面流暢度,與畫面流暢度成正比:幀率越大,畫面越流暢;幀率越小,畫面越有跳動感。若是碼率爲變量,則幀率也會影響體積,幀率越高,每秒鐘通過的畫面越多,須要的碼率也越高,體積也越大。 分辨率:影響圖像大小,與圖像大小成正比:分辨率越高,圖像越大;分辨率越低,圖像越小。
對於H5視頻播放,可使用 HLS(HTTP Live Streaming)協議播放直播流,iOS和 Android 都自然支持這種協議,配置簡單,直接使用 video 標籤便可。
使用 video在移動客戶端上播放直播視頻:
<video controls autoplay> <source src="xxx.m3u8" type="application/vnd.apple.mpegurl"/> </video>
HLS是一個由蘋果公司提出的基於HTTP的流媒體網絡傳輸協議。
HLS直播最大的不一樣在於,直播客戶端獲取到的,並非一個完整的數據流。
HLS協議在服務器端將直播數據流存儲爲連續的、很短時長的媒體文件(MPEG-TS格式),而客戶端則不斷的下載並播放這些小文件,由於服務器端老是會將最新的直播數據生成新的小文件,這樣客戶端只要不停的按順序播放從服務器獲取到的文件,就實現了直播。
因而可知,基本上能夠認爲,HLS是以點播的技術方式來實現直播。因爲數據經過HTTP協議傳輸,因此不用考慮防火牆或者代理的問題,並且分段文件的時長很短,客戶端能夠很快的選擇和切換碼率,以適應不一樣帶寬條件下的播放。不過HLS的這種技術特色決定了延遲通常老是會高於普通的流媒體直播協議。
每個 .m3u8 文件,分別對應若干個 ts 文件,這些 ts 文件纔是真正存放視頻的數據,m3u8 文件只是存放了一些 ts 文件的配置信息和相關路徑,當視頻播放時,.m3u8 是動態改變的,video 標籤會解析這個文件,並找到對應的 ts 文件來播放,因此通常爲了加快速度,.m3u8 放在 Web 服務器上,ts 文件放在 CDN 上。
支持的視頻流編碼爲H.264,音頻流編碼爲AAC。
簡單講就是把整個流分紅一個個小的,基於 HTTP 的文件來下載,每次只下載一些,前面提到了用於 H5 播放直播視頻時引入的一個 .m3u8 的文件,這個文件就是基於 HLS 協議,存放視頻流元數據的文件。
.m3u8 文件,其實就是以 UTF-8 編碼的 m3u 文件,這個文件自己不能播放,只是存放了播放信息的文本文件。 打開以後就是這個樣子:
#EXTM3U m3u文件頭,必須放在第一行 #EXT-X-MEDIA-SEQUENCE 第一個TS分片的序列號 #EXT-X-TARGETDURATION 每一個分片TS的最大的時長 #EXT-X-ALLOW-CACHE 是否容許cache #EXT-X-ENDLIST m3u8文件結束符 #EXTINF extra info,分片TS的信息,如時長,帶寬等
#EXTM3U #EXT-X-TARGETDURATION:11 #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:10.133333, fileSequence0.ts #EXTINF:10.000666, fileSequence1.ts #EXTINF:10.667334, fileSequence2.ts #EXTINF:9.686001, fileSequence3.ts #EXTINF:9.768665, fileSequence4.ts #EXTINF:10.000000, fileSequence5.ts #EXT-X-ENDLIST
ts 文件,就是存放視頻的文件:
HLS只請求基本的HTTP報文,與實時傳輸協議(RTP)不一樣,HLS能夠穿過任何容許HTTP數據經過的防火牆或者代理服務器。它也很容易使用內容分發網絡來傳輸媒體流。
咱們知道 hls 協議是將直播流分紅一段一段的小段視頻去下載播放的,因此假設列表裏面的包含5個 ts 文件,每一個 TS 文件包含5秒的視頻內容,那麼總體的延遲就是25秒。由於當你看到這些視頻時,主播已經將視頻錄製好上傳上去了,因此時這樣產生的延遲。固然能夠縮短列表的長度和單個 ts 文件的大小來下降延遲,極致來講能夠縮減列表長度爲1,而且 ts 的時長爲1s,可是這樣會形成請求次數增長,增大服務器壓力,當網速慢時回形成更多的緩衝,因此蘋果官方推薦的 ts 時長時10s,因此這樣就會大改有30s的延遲。因此服務器接收流,轉碼,保存,切塊,再分發給客戶端,這裏就延時的根本緣由。
更多關於延遲的問題能夠參考蘋果官方地址: https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
可是 H5 直播視頻卻有一些不可替代的優點:
RTMP協議是應用層協議,是要靠底層可靠的傳輸層協議(一般是TCP)來保證信息傳輸的可靠性的。在基於傳輸層協議的連接創建完成後,RTMP協議也要客戶端和服務器經過「握手」來創建基於傳輸層連接之上的NetConnection連接,在Connection連接上會傳輸一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令會建立一個NetStream連接,用於傳輸具體的音視頻數據和控制這些信息傳輸的命令信息。他們的關係如圖所示:
RTMP協議傳輸時會對數據作本身的格式化,這種格式的消息咱們稱之爲RTMP Message,而實際傳輸的時候爲了更好地實現多路複用、分包和信息的公平性,發送端會把Message劃分爲帶有Message ID的Chunk,每一個Chunk多是一個單獨的Message,也多是Message的一部分,在接受端會根據chunk中包含的data的長度,message id和message的長度把chunk還原成完整的Message,從而實現信息的收發。
RTMP在收發數據的時候並非以Message爲單位的,而是把Message拆分紅Chunk發送,並且必須在一個Chunk發送完成以後才能開始發送下一個Chunk。每一個Chunk中帶有MessageID表明屬於哪一個Message,接受端也會按照這個id來將chunk組裝成Message。
舉個例子
chunk表示例1
首先包含第一個Message的chunk的Chunk Type爲0,由於它沒有前面可參考的chunk,timestamp爲1000。type爲0的header佔用11個字節,假定CSID爲3<127,所以Basic Header佔用1個字節,再加上Data的32個字節,所以第一個chunk共44=11+1+32個字節。
第二個chunk和第一個chunk的CSID,TypeId,Data的長度都相同,所以採用Chunk Type=2,timestamp delta=1020-1000=20,所以第二個chunk佔用36=3+1+32個字節。
第三個chunk和第二個chunk的CSID,TypeId,Data的長度和時間戳差都相同,所以採用Chunk Type=3省去所有Message Header的信息,佔用33=1+32個字節。
第四個chunk和第三個chunk狀況相同,也佔用33=1+32個字節。
最後實際發送的chunk以下:
chunk表示例2 注意到Data的Length=307>128,所以這個Message要切分紅幾個chunk發送,第一個chunk的Type=0,Timestamp=1000,承擔128個字節的Data,所以共佔用140=11+1+128個字節。
第二個chunk也要發送128個字節,其餘字段也同第一個chunk,所以採用Chunk Type=3,此時時間戳也爲1000,共佔用129=1+128個字節。
第三個chunk要發送的Data的長度爲307-128-128=51個字節,仍是採用Type=3,共佔用1+51=52個字節。
最後實際發送的chunk以下:
Q:爲何RTMP要將Message拆分紅不一樣的Chunk呢? A:經過拆分,數據量較大的Message能夠被拆分紅較小的「Message」,這樣就能夠避免優先級低的消息持續發送阻塞優先級高的數據,好比在視頻的傳輸過程當中,會包括視頻幀,音頻幀和RTMP控制信息,若是持續發送音頻數據或者控制數據的話可能就會形成視頻幀的阻塞,而後就會形成看視頻時最煩人的卡頓現象。同時對於數據量較小的Message,能夠經過對Chunk Header的字段來壓縮信息,從而減小信息的傳輸量。
阻塞 vs. CPU Chunk的默認大小是128字節,在傳輸過程當中,經過一個叫作Set Chunk Size的控制信息能夠設置Chunk數據量的最大值,在發送端和接受端會各自維護一個Chunk Size,能夠分別設置這個值來改變本身這一方發送的Chunk的最大大小。大一點的Chunk減小了計算每一個chunk的時間從而減小了CPU的佔用率,可是它會佔用更多的時間在發送上,尤爲是在低帶寬的網絡狀況下,極可能會阻塞後面更重要信息的傳輸。小一點的Chunk能夠減小這種阻塞問題,但小的Chunk會引入過多額外的信息(Chunk中的Header),少許屢次的傳輸也可能會形成發送的間斷致使不能充分利用高帶寬的優點,所以並不適合在高比特率的流中傳輸。在實際發送時應對要發送的數據用不一樣的Chunk Size去嘗試,經過抓包分析等手段得出合適的Chunk大小,而且在傳輸過程當中能夠根據當前的帶寬信息和實際信息的大小動態調整Chunk的大小,從而儘可能提升CPU的利用率並減小信息的阻塞機率。
來源於《帶你吃透RTMP》
播放一個RTMP協議的流媒體須要通過如下幾個步驟:握手,創建鏈接,創建流,播放。RTMP鏈接都是以握手做爲開始的。創建鏈接階段用於創建客戶端與服務器之間的「網絡鏈接」;創建流階段用於創建客戶端與服務器之間的「網絡流」;播放階段用於傳輸視音頻數據。
一個RTMP鏈接以握手開始,雙方分別發送大小固定的三個數據塊
理論上來說只要知足以上條件,如何安排6個Message的順序都是能夠的,但實際實現中爲了在保證握手的身份驗證功能的基礎上儘可能減小通訊的次數,通常的發送順序是這樣的:
服務器在收到客戶端發送的鏈接請求後發送以下信息: 主要是告訴客戶端確認窗口大小,設置節點帶寬,而後服務器把「鏈接」鏈接到指定的應用並返回結果,「網絡鏈接成功」。而且返回流開始的的消息(Stream Begin 0)。
對於直播中的用戶交互大體能夠分爲:
對於彈幕來講,要稍微複雜一些,可能須要關注如下幾點:
該組件主要適用於基於Html5的web 大羣互動直播場景。具有以下特色: 1)支持匿名身份入羣,粉絲與主播進行親密互動 2)支持多人聊天,主播同一個賬號多標籤頁收發消息,粉絲再多也不用愁 3)支持多種聊天方式,文本,表情,紅包,點贊,想怎麼互動就怎麼互動 4)支持不一樣優先級消息的頻率控制,一鍵在手,權利盡在掌握中 5)對互動直播場景進行了專門的優化,參與人數多,消息量再大也能從容應對
目前較爲成熟的直播產品,大體都是以 Server 端、 H5直播前端 和 Native(Android,iOS)搭配實現直播。
主要從android客戶端出發,從最初的錄製視頻到客戶端觀看直播的整個流程,給出了各個技術點的概要和解決方案,從0到1完成了簡單的直播實現。從0到1易,從1到100還有更多的技術細節有待研究。
更多精彩內容歡迎關注bugly的微信公衆帳號:
騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!