在這個全民娛樂的時代,直播已經火得不要不要的,各大公司都有自己的直播產品。本文主要從直播的一些基本知識,一步步打造直播app。直播那麼火的背後有什麼樣的技術支撐呢? ...
本文來自於騰訊bugly開發者社區,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/5811d42e7fd6ec467453bf58
作者:李智文
概要
分享內容:
互聯網內容載體變遷歷程,文字——圖片/聲音——視頻——VR/AR——…….。從直播1.0秀場時代(YY),2.0游戲直播(鬥魚、虎牙、熊貓)到如今全民直播3.0泛生活娛樂時代(映客、花椒),國外直播app(Meerkat 、Periscope),隨著VA/AR/MR提出的沉浸式視聽體驗,直播4.0時代很快就能到來。
在這個全民娛樂的時代,直播已經火得不要不要的,各大公司都有自己的直播產品。本文主要從直播的一些基本知識,一步步打造直播app。直播那麼火的背後有什麼樣的技術支撐呢?
先將這些APP按照視頻網站按照視頻網站、彈幕視頻、直播平臺、線上秀場、移動短視頻、移動直播來劃分類別。再按照內容和社交這個維度來進行區分,可以明顯看出視頻網站、彈幕網站和直播平臺更偏內容,他們對內容的需求更加高,用戶在上面進行社交沉澱相對比較淺。
而後面三者更加偏向社交,他們強調人而不強調內容。所以短期內不會有大的競爭關係,只是前三類、後三者之間的競爭會出現。
大體框架
基本是下圖這個套路:
錄製->編碼->網路傳輸->解碼->播放
以上為直播的整體流程,根據該流程分為以下技術點:
- 怎樣錄製直播視頻
- 怎樣實時上傳直播視頻
- 怎樣播放直播視頻
- 直播間的用戶是如何交互
一、移動視頻直播發展
PC直播(固定場所)——>移動端(形式自由)。
隨著越來越多的直播類 App 上線,移動直播進入了前所未有的爆發階段,目前大多數移動直播以 Native 客戶端為主。但是H5端的直播在移動直播端也承載著不可替代的作用,例如 H5 有著傳播快,易發佈的優勢。
完整的直播包括:
- 視頻錄製端
電腦上的音視頻輸入設備或者手機端的攝像頭或者麥克風,目前以移動端的手機視頻為主。 - 視頻播放端
可以是電腦上的播放器,手機端的 Native 播放器,還有 H5 的 video 標簽等。 - 流媒體伺服器端
用來接受視頻錄製端提供的視頻源,同時提供給視頻播放端流服務。目前開源的流媒體有RED5,CRTMPD,NGINX-RTMP,SRS。
二、錄製視頻
如何生產視頻數據
封裝格式的主要作用是把視頻碼流和音頻碼流按照一定的格式存儲在一個文件中。
為什麼要分封裝格式和視頻編碼格式呢?
這個其實跟網路分七層模型一個原理。解耦和,降低依賴,底層給上層提供基礎功能,底層和上層都都可以單獨擴展,可以以多種方案組合編碼與封裝,比如MP4與H264、MP4與MPEG、TS與H264等等。比如這裡面的這邊文章的編碼就只負責將最原始的音頻和視頻數據就行壓縮,而壓縮完的數據要怎麼組織就拜托給上層的封裝,封裝接到視頻音頻數據負責給數據編號,指定同步協議,加入字幕等操作。經過封裝後,得到的就是可以播放的上面提到的視頻文件MP4或者MKV等等。把這個過程反過來就是上圖描述的視頻播放的過程。
1、流媒體源
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)來實現,效果會好一些。
2、編碼
推薦Andorid4.3(API18)或以上使用硬編,以下版本使用軟編;iOS使用全硬編方案。
- 軟編碼:
libffmpeg - 硬編碼:
MediaCodec(sdk level 16+, Android 4.1, 4.1.1, the JELLY_BEAN)
3、封裝
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為例的推流的流程圖:
三、視頻推流(Stream)
如何推
往哪裡推
1、協議
國內常見公開的直播協議有幾個:RTMP、HDL(HTTP-FLV)、HLS、RTP。
RTMP
Real Time Messaging Protocol是 Macromedia 開發的一套視頻直播協議,現在屬於 Adobe。
使用RTMP技術的流媒體系統有一個非常明顯的特點:使用 Flash Player 作為播放器客戶端,而Flash Player 現在已經安裝在了全世界將近99%的PC上,因此一般情況下收看RTMP流媒體系統的視音頻是不需要安裝插件的。用戶只需要打開網頁,就可以直接收看流媒體。
和 HLS 一樣都可以應用於視頻直播,區別是 RTMP 基於 flash 無法在 iOS 的瀏覽器里播放,但是實時性比 HLS 要好。所以一般使用這種協議來上傳視頻流,也就是視頻流推送到伺服器。
rtmp現在大部分國外的CDN已不支持,在國內流行度很高。原因有幾個方面:
- 開源軟體和開源庫的支持穩定完整。如鬥魚主播常用的OBS軟體,開源的librtmp庫,服務端有nginx-rtmp插件。
- 播放端安裝率高。只要瀏覽器支持FlashPlayer就能播放RTMP的直播。相對其他協議而言,RTMP協議初次建立連接的時候握手過程過於複雜(RTMP協議本身的交互是基於TCP),視不同的網路狀況會帶來給首開帶來100ms以上的延遲。基於RTMP延遲在2~5秒。
HTTP-FLV
即使用HTTP協議流式的傳輸媒體內容,直接向後臺上傳編碼後的流媒體數據。相對於RTMP,HTTP更簡單和廣為人知,而且不擔心被Adobe的專利綁架。內容延遲同樣可以做到2~5秒,打開速度更快,因為HTTP本身沒有複雜的狀態交互。所以從延遲角度來看,HTTP-FLV要優於RTMP。
SRS2.0支持該協議:GitHub
HLS
即Http Live Streaming,是由蘋果提出基於HTTP的流媒體傳輸協議。HLS有一個非常大的優點:HTML5可以直接打開播放;這個意味著可以把一個直播鏈接通過微信等轉發分享,不需要安裝任何獨立的APP,有瀏覽器即可,所以流行度很高。社交直播APP,HLS可以說是剛需 。
Issue:SRS3.0提出了一種增強型HLS+
RTP
Real-time Transport Protocol,用於Internet上針對多媒體數據流的一種傳輸層協議。
實際應用場景下經常需要RTCP(RTP Control Protocol)配合來使用,可以簡單理解為RTCP傳輸交互控制的信令,RTP傳輸實際的媒體數據。
RTP在視頻監控、視頻會議、IP電話上有廣泛的應用,因為視頻會議、IP電話的一個重要的使用體驗:內容實時性強。
對比與上述3種或實際是2種協議,RTP和它們有一個重要的區別就是預設是使用UDP協議來傳輸數據,而RTMP和HTTP-FLV是基於TCP協議傳輸。
- UDP:單個數據報,不用建立連接,簡單,不可靠,會丟包,會亂序;
- TCP:流式,需要建立連接,複雜,可靠 ,有序。
實時音視頻流的場景不需要可靠保障,因此也不需要有重傳的機制,實時的看到圖像聲音,網路抖動時丟了一些內容,畫面模糊和花屏,完全不重要。TCP為了重傳會造成延遲與不同步,如某一截內容因為重傳,導致1秒以後才到,那麼整個對話就延遲了1秒,隨著網路抖動,延遲還會增加成2秒、3秒,如果客戶端播放是不加以處理將嚴重影響直播的體驗。
是否有除了HLS外更低延遲的方案?
HLS的優點點是顯而易見的:移動端無需安裝APP使用相容HTML5的瀏覽器打開即可觀看,所有主流的移動端瀏覽器基本都支持HTML5,在直播的傳播和體驗上有巨大的優勢。
下麵是 HTTP-FLV、HLS 、 RTMP 的對比:
2、推流
所謂推流,就是將我們已經編碼好的音視頻數據發往視頻流伺服器中,常用的第三方庫 librtmp-iOS 進行推流,librtmp 封裝了一些核心的 API 供使用者調用。例如推流 API 等等,配置伺服器地址,即可將轉碼後的視頻流推往伺服器。一般的推流伺服器都配置了伺服器端信息。
推流SDK
百度雲推流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 伺服器。
- 安裝 nginx 的 RTMP 擴展,目前使用比較多的是 https://github.com/arut/nginx-rtmp-module
- 配置 nginx 的 conf 文件
- 重啟 nginx,將 RTMP 的推流地址寫為 rtmp://ip:1935/hls/mystream, 其中 hls_path 表示生成的 .m3u8 和 ts 文件所存放的地址,hls_fragment 表示切片時長,mysteam 表示一個實例,即將來要生成的文件名可以先自己隨便設置一個。
更多配置可以參考:https://github.com/arut/nginx-rtmp-module/wiki/
下麵是 nginx 的配置文件
騰訊雲直播
[後臺SDK]主要是調用騰訊雲API。
[伺服器API]提供了直播控制台api概覽:
- 創建直播頻道 CreateLVBChannel
- 查詢直播頻道列表 DescribeLVBChannelList
- 查詢直播頻道詳情 DescribeLVBChannel
- 修改直播頻道 ModifyLVBChannel
- 批量啟用直播頻道 StartLVBChannel
- 批量停止直播頻道 StopLVBChannel
- 查詢直播頻道當前併發收看數 DescribeLVBOnlineUsers
- 刪除直播頻道 DeleteLVBChannel
- 創建錄製任務 CreateRecord
- 終止錄製任務 StopRecord
- 查詢已錄製分片列表 DescribeRecord
- 創建截圖任務 CreateLVBShot
- 終止截圖任務 StopLVBShot
- 查看隊列消息 DescribeQueueLog
騰訊雲直播方案整體流程
方案根據騰訊雲的快速對接,最終形成閉環邏輯。
- APP
- 視頻源推流
- 向後臺發起創建直播頻道請求
- 向後臺發起停止直播請求
- 後臺
- 向騰訊雲發起創建、刪除(刪除前先關閉)直播頻道請求
- 直播頻道緩存隊列,處理僵屍頻道
- 向APP客戶端推送直播URL
- Web
- PC端的流視頻播放器
- 移動客戶端的流視頻播放器
流程圖
- Step1:創建頻道
客戶端發起直播請求,後臺調用CreateLVBChannel,由返回的channel_id調用DescribeLVBChannel查看頻道信息。
後臺向客戶端返回推流url和Web直播地址(非flv流視頻地址)。
- Step2:SDK推流
推流SDK - Step3:刪除頻道
APP端推流結束,向後臺發送請求刪除頻道,只有關閉的頻道是可以刪除的,所以後臺刪除一個頻道之前,要先通過停止直播頻道介面StopLVBChannel,先將頻道狀態置為停止,之後在調用刪除直播頻道介面DeleteLVBChannel對頻道進行刪除。
四、播放直播視頻
如何看
下載直播視頻有以下方式:
- HLS
- rtmp
- flv
好看的指標參數
碼率:影響體積,與體積成正比:碼率越大,體積越大;碼率越小,體積越小。
幀率:影響畫面流暢度,與畫面流暢度成正比:幀率越大,畫面越流暢;幀率越小,畫面越有跳動感。如果碼率為變數,則幀率也會影響體積,幀率越高,每秒鐘經過的畫面越多,需要的碼率也越高,體積也越大。
解析度:影響圖像大小,與圖像大小成正比:解析度越高,圖像越大;解析度越低,圖像越小。
1、HLS
對於H5視頻播放,可以使用 HLS(HTTP Live Streaming)協議播放直播流,iOS和 Android 都天然支持這種協議,配置簡單,直接使用 video 標簽即可。
使用 video在移動客戶端上播放直播視頻:
<video controls autoplay>
<source src="xxx.m3u8" type="application/vnd.apple.mpegurl"/>
</video>
HTTP Live Streaming
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 協議,存放視頻流元數據的文件。
- HLS的分段策略,基本上推薦是10秒一個分片,當然,具體時間還要根據分好後的分片的實際時長做標註
- 為了緩存等方面的原因,在索引文件中會保留最新的三個分片地址,以“滑動視窗”的形式進行刷新。
.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 的請求播放流程
- HTTP 請求 m3u8 的 url。
- 服務端返回一個 m3u8 的播放列表,這個播放列表是實時更新的,一般一次給出5段數據的 url。
- 客戶端解析 m3u8 的播放列表,再按序請求每一段的 url,獲取 ts 數據流。
HLS直播延時
我們知道 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
HLS優勢
但是 H5 直播視頻卻有一些不可替代的優勢:
- 傳播性好,利於分享等操作。
- 可以動態發佈,有利於實時迭代產品需求並迅速上線。
- 不用安裝 App,直接打開瀏覽器即可。
2、RTMP
RTMP協議是應用層協議,是要靠底層可靠的傳輸層協議(通常是TCP)來保證信息傳輸的可靠性的。在基於傳輸層協議的鏈接建立完成後,RTMP協議也要客戶端和伺服器通過“握手”來建立基於傳輸層鏈接之上的NetConnection鏈接,在Connection鏈接上會傳輸一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令會創建一個NetStream鏈接,用於傳輸具體的音視頻數據和控制這些信息傳輸的命令信息。他們的關係如圖所示:
RTMP Message
RTMP協議傳輸時會對數據做自己的格式化,這種格式的消息我們稱之為RTMP Message,而實際傳輸的時候為了更好地實現多路復用、分包和信息的公平性,發送端會把Message劃分為帶有Message ID的Chunk,每個Chunk可能是一個單獨的Message,也可能是Message的一部分,在接受端會根據chunk中包含的data的長度,message id和message的長度把chunk還原成完整的Message,從而實現信息的收發。
- Basic Header(基本的頭信息)
chunk stream ID(流通道Id)和chunk type(chunk的類型,2位fmt),chunk stream id一般被簡寫為CSID,用來唯一標識一個特定的流通道,chunk type決定了後面Message Header的格式。長度有1、2或3個位元組 - Message Header(消息的頭信息)
Message Header的格式和長度取決於Basic Header的chunk type,共有4種不同的格式,由上面所提到的Basic Header中的fmt欄位控制。包含timestamp,timestamp delta,message length,message type id,msg stream id,和0(表示與上一個相同)。- timestamp(時間戳):占用3個位元組,因此它最多能表示到16777215=0xFFFFFF=2
24-1, 當它的值超過這個最大值時,這三個位元組都置為1,這樣實際的timestamp會轉存到Extended Timestamp欄位中,接受端在判斷timestamp欄位24個位都為1時就會去Extended timestamp中解析實際的時間戳。 - message length(消息數據的長度):占用3個位元組,表示實際發送的消息的數據如音頻幀、視頻幀等數據的長度,單位是位元組。註意這裡是Message的長度,也就是chunk屬於的Message的總數據長度,而不是chunk本身Data的數據的長度。
- message type id(消息的類型id):占用1個位元組,表示實際發送的數據的類型,如8代表音頻數據、9代表視頻數據。
- msg stream id(消息的流id):占用4個位元組,表示該chunk所在的流的ID,和Basic Header的CSID一樣,它採用小端存儲的方式,
- timestamp(時間戳):占用3個位元組,因此它最多能表示到16777215=0xFFFFFF=2
- Extended Timestamp(擴展時間戳)
4個位元組,當擴展時間戳啟用時,timestamp欄位或者timestamp delta要全置為1,表示應該去擴展時間戳欄位來提取真正的時間戳或者時間戳差。註意擴展時間戳存儲的是完整值,而不是減去時間戳或者時間戳差的值。 - Chunk Data(塊數據)
用戶層面上真正想要發送的與協議無關的數據,長度在(0,chunkSize]之間。
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連接都是以握手作為開始的。建立連接階段用於建立客戶端與伺服器之間的“網路連接”;建立流階段用於建立客戶端與伺服器之間的“網路流”;播放階段用於傳輸視音頻數據。
握手(HandShake)
一個RTMP連接以握手開始,雙方分別發送大小固定的三個數據塊
- 握手開始於客戶端發送C0、C1塊。伺服器收到C0或C1後發送S0和S1。
- 當客戶端收齊S0和S1後,開始發送C2。當伺服器收齊C0和C1後,開始發送S2。
- 當客戶端和伺服器分別收到S2和C2後,握手完成。
理論上來講只要滿足以上條件,如何安排6個Message的順序都是可以的,但實際實現中為了在保證握手的身份驗證功能的基礎上儘量減少通信的次數,一般的發送順序是這樣的:
- Client發送C0+C1到Sever
- Server發送S0+S1+S2到Client
- Client發送C2到Server,握手完成
建立網路連接(NetConnection)
- 客戶端發送命令消息中的“連接”(connect)到伺服器,請求與一個服務應用實例建立連接。
- 伺服器接收到連接命令消息後,發送確認視窗大小(Window Acknowledgement Size)協議消息到客戶端,同時連接到連接命令中提到的應用程式。
- 伺服器發送設置帶寬(Set Peer Bandwitdh)協議消息到客戶端。
- 客戶端處理設置帶寬協議消息後,發送確認視窗大小(Window Acknowledgement Size)協議消息到伺服器端。
- 伺服器發送用戶控制消息中的“流開始”(Stream Begin)消息到客戶端。
- 伺服器發送命令消息中的“結果”(_result),通知客戶端連接的狀態。
- 客戶端在收到伺服器發來的消息後,返回確認視窗大小,此時網路連接創建完成。
伺服器在收到客戶端發送的連接請求後發送如下信息:
主要是告訴客戶端確認視窗大小,設置節點帶寬,然後伺服器把“連接”連接到指定的應用並返回結果,“網路連接成功”。並且返迴流開始的的消息(Stream Begin 0)。
建立網路流(NetStream)
- 客戶端發送命令消息中的“創建流”(createStream)命令到伺服器端。
- 伺服器端接收到“創建流”命令後,發送命令消息中的“結果”(_result),通知客戶端流的狀態。
推流流程
- 客戶端發送publish推流指令。
- 伺服器發送用戶控制消息中的“流開始”(Stream Begin)消息到客戶端。
- 客戶端發送元數據(解析度、幀率、音頻採樣率、音頻碼率等等)。
- 客戶端發送音頻數據。
- 客戶端發送伺服器發送設置塊大小(ChunkSize)協議消息。
- 伺服器發送命令消息中的“結果”(_result),通知客戶端推送的狀態。
- 客戶端收到後,發送視頻數據直到結束。
播流流程
- 客戶端發送命令消息中的“播放”(play)命令到伺服器。
- 接收到播放命令後,伺服器發送設置塊大小(ChunkSize)協議消息。
- 伺服器發送用戶控制消息中的“streambegin”,告知客戶端流ID。
- 播放命令成功的話,伺服器發送命令消息中的“響應狀態” NetStream.Play.Start & NetStream.Play.reset,告知客戶端“播放”命令執行成功。
- 在此之後伺服器發送客戶端要播放的音頻和視頻數據。
五、直播中的用戶交互
對於直播中的用戶交互大致可以分為:
- 送禮物
- 發表評論或者彈幕
- 對於送禮物,在 H5 端可以利用 DOM 和 CSS3 實現送禮物邏輯和一些特殊的禮物動畫,實現技術難點不大。
對於彈幕來說,要稍微複雜一些,可能需要關註以下幾點:
- 彈幕實時性,可以利用 webscoket 來實時發送和接收新的彈幕並渲染出來。
- 對於不支持 webscoket 的瀏覽器來說,只能降級為長輪詢或者前端定時器發送請求來獲取實時彈幕。
- 彈幕渲染時的動畫和碰撞檢測(即彈幕不重疊)等等
Html5直播聊天室組件
該組件主要適用於基於Html5的web 大群互動直播場景。具備如下特點:
1)支持匿名身份入群,粉絲與主播進行親密互動
2)支持多人聊天,主播同一個帳號多標簽頁收發消息,粉絲再多也不用愁
3)支持多種聊天方式,文本,表情,紅包,點贊,想怎麼互動就怎麼互動
4)支持不同優先順序消息的頻率控制,一鍵在手,權利盡在掌握中
5)對互動直播場景進行了專門的優化,參與人數多,消息量再大也能從容應對
前端技術點
- 秒開
- 時延
- 流暢
- 清晰度
六、總結
目前較為成熟的直播產品,大致都是以 Server 端、 H5直播前端 和 Native(Android,iOS)搭配實現直播。
主要從android客戶端出發,從最初的錄製視頻到客戶端觀看直播的整個流程,給出了各個技術點的概要和解決方案,從0到1完成了簡單的直播實現。從0到1易,從1到100還有更多的技術細節有待研究。
更多精彩內容歡迎關註bugly的微信公眾賬號:
騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精准定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在發佈後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方操作系統,鵝廠的工程師都在使用,快來加入我們吧!