@TOC
1. RTMP協(xié)議簡介
RTMP協(xié)議是一個互聯(lián)網(wǎng)TCP/IP五層體系結(jié)構(gòu)中應(yīng)用層的協(xié)議擂橘。RTMP協(xié)議中基本的數(shù)據(jù)單元稱為消息(Message)滞谢。當(dāng)RTMP協(xié)議在互聯(lián)網(wǎng)中傳輸數(shù)據(jù)的時候吃谣,消息會被拆分成更小的單元,稱為消息塊(Chunk)。RTMP 是目前主流的流媒體傳輸協(xié)議,廣泛用于直播領(lǐng)域存和,可以說市面上絕大多數(shù)的直播產(chǎn)品都采用了這個協(xié)議。
RTMP協(xié)議就像一個用來裝數(shù)據(jù)包的容器衷旅,這些數(shù)據(jù)可以是AMF格式的數(shù)據(jù),也可以是FLV中的視/音頻數(shù)據(jù)捐腿。一個單一的連接可以通過不同的通道傳輸多路網(wǎng)絡(luò)流。這些通道中的包都是按照固定大小的包傳輸?shù)摹?/p>
- 優(yōu)點
- CDN 支持良好柿顶,主流的 CDN 廠商都支持
- 協(xié)議簡單茄袖,在各平臺上實現(xiàn)容易
- 缺點
- 基于 TCP ,傳輸成本高嘁锯,在弱網(wǎng)環(huán)境丟包率高的情況下問題顯著
- 不支持瀏覽器推送
- Adobe 私有協(xié)議宪祥,Adobe 已經(jīng)不再更新
2. RTMP協(xié)議基本概念
2.1 消息相關(guān)概念
RTMP傳輸?shù)臄?shù)據(jù)的基本單元為Message,但是實際上傳輸?shù)淖钚卧荂hunk(消息塊)家乘,因為RTMP協(xié)議為了提升傳輸速度蝗羊,在傳輸數(shù)據(jù)的時候,會把Message拆分開來仁锯,形成更小的塊肘交,這些塊就是Chunk。
2.1.1 消息
消息是RTMP協(xié)議中基本的數(shù)據(jù)單元扑馁。不同種類的消息包含不同的Message Type ID,代表不同的功能凉驻。RTMP協(xié)議中一共規(guī)定了十多種消息類型腻要,分別發(fā)揮著不同的作用。
1-7的消息用于協(xié)議控制涝登,這些消息一般是RTMP協(xié)議自身管理要使用的消息雄家,用戶一般情況下無需操作其中的數(shù)據(jù)
Message Type ID為8,9的消息分別用于傳輸音頻和視頻數(shù)據(jù)
Message Type ID為15-20的消息用于發(fā)送AMF編碼的命令胀滚,負(fù)責(zé)用戶與服務(wù)器之間的交互趟济,比如播放乱投,暫停等等
消息首部(Message Header)有四部分組成:標(biāo)志消息類型的Message Type ID,標(biāo)志消息長度的Payload Length顷编,標(biāo)識時間戳的Timestamp戚炫,標(biāo)識消息所屬媒體流的Stream ID
下面針對上圖的消息結(jié)構(gòu)體進(jìn)行簡要分析:
- Message Type:它是一個消息類型的ID,通過該ID接收方可以判斷接收到的數(shù)據(jù)的類型媳纬,從而做相應(yīng)的處理双肤。Message Type ID在1-7的消息用于協(xié)議控制,這些消息一般是RTMP協(xié)議自身管理要使用的消息钮惠,用戶一般情況下無需操作其中的數(shù)據(jù)茅糜。
- Message Type ID為8,9的消息分別用于傳輸音頻和視頻數(shù)據(jù)素挽。Message Type ID為15-20的消息用于發(fā)送AMF編碼的命令蔑赘,負(fù)責(zé)用戶與服務(wù)器之間的交互,比如播放预明,暫停等缩赛。
- Playload Length: 消息負(fù)載的長度,即音視頻相關(guān)信息的的數(shù)據(jù)長度贮庞,4個字節(jié)
- TimeStamp:時間戳峦筒,3個字節(jié)。
- Stream ID:消息的唯一標(biāo)識窗慎。拆分消息成Chunk時添加該ID物喷,從而在還原時根據(jù)該ID識別Chunk屬于哪個消息。
- Message Body:消息體遮斥,承載了音視頻等信息峦失。
2.1.2. 消息塊
在網(wǎng)絡(luò)上傳輸數(shù)據(jù)時,消息需要被拆分成較小的數(shù)據(jù)塊术吗,才適合在相應(yīng)的網(wǎng)絡(luò)環(huán)境上傳輸尉辑。RTMP協(xié)議中規(guī)定,消息在網(wǎng)絡(luò)上傳輸時被拆分成消息塊(Chunk)较屿。
消息塊首部(Chunk Header)有三部分組成:
- 用于標(biāo)識本塊的Chunk Basic Header
- 用于標(biāo)識本塊負(fù)載所屬消息的Chunk Message Header
- 以及當(dāng)時間戳溢出時才出現(xiàn)的Extended Timestamp
通過上圖可以看出隧魄,消息塊在結(jié)構(gòu)上與與消息類似,有Header和Body隘蝎。
下面對圖的每個部分簡要介紹:
- Basic Header:基本的頭部信息购啄,在頭部信息里面包含了chunk stream ID(流通道Id,用來標(biāo)識指定的通道)和chunk type(chunk的類型)嘱么。
- Message Header:消息的頭部信息狮含,包含了要發(fā)送的實際信息(可能是完整的,也可能是一部分)的描述信息。Message Header的格式和長度取決于Basic Header的chunk type几迄。
- Extended TimeStamp:擴展時間戳蔚龙。
- Chunk Data:塊數(shù)據(jù)。
注意: RTMP在傳輸數(shù)據(jù)的時候映胁,發(fā)送端會把需要傳輸?shù)拿襟w數(shù)據(jù)封裝成消息木羹,然后把消息拆分成消息塊,再一個一個進(jìn)行傳輸屿愚。接收端收到消息塊后汇跨,根據(jù)Message Stream ID重新將消息塊進(jìn)行組裝、組合成消息妆距,再解除該消息的封裝處理就可以還原出媒體數(shù)據(jù)穷遂。由此可以看出,RTMP收發(fā)數(shù)據(jù)是以Chunk為單位娱据,而不是以Message為單位蚪黑。需要注意的是,RTMP發(fā)送Chunk必須是一個一個發(fā)送中剩,后面的Chunk必須等前面的Chunk發(fā)送完成忌穿。
2.1.3. 消息分塊
在消息被分割成幾個消息塊的過程中,消息負(fù)載部分(Message Body)被分割成大小固定的數(shù)據(jù)塊(默認(rèn)是128字節(jié)结啼,最后一個數(shù)據(jù)塊可以小于該固定長度)掠剑,并在其首部加上消息塊首部(Chunk Header),就組成了相應(yīng)的消息塊郊愧。消息分塊過程如圖5所示朴译,一個大小為307字節(jié)的消息被分割成128字節(jié)的消息塊(除了最后一個)。
RTMP傳輸媒體數(shù)據(jù)的過程中属铁,發(fā)送端首先把媒體數(shù)據(jù)封裝成消息眠寿,然后把消息分割成消息塊,最后將分割后的消息塊通過TCP協(xié)議發(fā)送出去焦蘑。接收端在通過TCP協(xié)議收到數(shù)據(jù)后盯拱,首先把消息塊重新組合成消息,然后通過對消息進(jìn)行解封裝處理就可以恢復(fù)出媒體數(shù)據(jù)例嘱。
2.2. RTMP中的邏輯結(jié)構(gòu)
RTMP協(xié)議規(guī)定狡逢,播放一個流媒體有兩個前提步驟
第一步,建立一個網(wǎng)絡(luò)連接(NetConnection)
第二步拼卵,建立一個網(wǎng)絡(luò)流(NetStream)奢浑。
其中,網(wǎng)絡(luò)連接代表服務(wù)器端應(yīng)用程序和客戶端之間基礎(chǔ)的連通關(guān)系间学。網(wǎng)絡(luò)流代表了發(fā)送多媒體數(shù)據(jù)的通道。服務(wù)器和客戶端之間只能建立一個網(wǎng)絡(luò)連接,但是基于該連接可以創(chuàng)建很多網(wǎng)絡(luò)流低葫。他們的關(guān)系如圖所示:
2.5.
2.6.
2.7.
3. RTMP協(xié)議流程
總的流程圖如下:
3.1. 連接流程
播放一個RTMP協(xié)議的流媒體需要經(jīng)過以下幾個步驟:
- 握手
- 建立連接
- 建立流
- 播放
RTMP連接都是以握手作為開始的详羡。建立連接階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)連接”;建立流階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)流”嘿悬;播放階段用于傳輸視音頻數(shù)據(jù)实柠。
下面來詳細(xì)分析一下這幾個過程都做了一些什么東東
3.1.1.握手
在rtmp連接建立后,服務(wù)端與客戶端需要通過3次交換報文完成握手,握手其他的協(xié)議不同,是由三個靜態(tài)大小的塊,而不是可變大小的塊組成的,客戶端與服務(wù)器發(fā)送相同的三個chunk,客戶端發(fā)送c0,c1,c2,服務(wù)端發(fā)送s0,s1,s2。
- 發(fā)送規(guī)則
握手開始于客戶端發(fā)送 C0善涨,C1 塊窒盐。
在發(fā)送 C2 之前客戶端必須等待接收 S1 。
在發(fā)送任何數(shù)據(jù)之前客戶端必須等待接收 S2钢拧。
服務(wù)端在發(fā)送 S0 和 S1 之前必須等待接收 C0蟹漓,也可以等待接收 C1。
服務(wù)端在發(fā)送 S2 之前必須等待接收 C1源内。
服務(wù)端在發(fā)送任何數(shù)據(jù)之前必須等待接收 C2葡粒。
- 數(shù)據(jù)格式
C0與S0
C0和S0的長度是一個字節(jié),在 S0 中這個字段表示服務(wù)器選擇的 RTMP 版本膜钓。rtmp1.0規(guī)范所定義的版本是 3嗽交;0-2 是早期產(chǎn)品所用的,已被丟棄颂斜;4-31保留在未來使用夫壁;32-255 不允許使用(為了區(qū)分其他以某一字符開始的文本協(xié)議)。如果服務(wù)無法識別客戶端請求的版本沃疮,應(yīng)該返回 3 盒让。客戶端可以選擇減到版本 3 或選擇取消握手忿磅。
C1與S1
C1 和 S1 有 1536 字節(jié)長糯彬,由下列字段組成:
時間:4 字節(jié) 本字段包含時間戳。該時間戳應(yīng)該是發(fā)送這個數(shù)據(jù)塊的端點的后續(xù)塊的時間起始點葱她×冒牵可以是 0,* 或其他的 任何值吨些。為了同步多個流搓谆,端點可能發(fā)送其塊流的當(dāng)前值。
零:4 字節(jié) 本字段必須是全零豪墅。
隨機數(shù)據(jù):1528 字節(jié)泉手。 本字段可以包含任何值。 因為每個端點必須用自己初始化的握手和對端初始化的握 手來區(qū)分身份偶器,所以這個數(shù)據(jù)應(yīng)有充分的隨機性斩萌。但是并不需要加密安全的隨機值缝裤,或者動態(tài)值
C2與S2
C2 和 S2 消息有 1536 字節(jié)長。只是 S1 和 C1 的回復(fù)颊郎。本消息由下列字段組成憋飞。
時間:4 字節(jié) 本字段必須包含對等段發(fā)送的時間(對 C2 來說是 S1,對 S2 來說是 C1)姆吭。
時間 2:4 字節(jié) 本字段必須包含先前發(fā)送的并被對端讀取的包的時間戳榛做。
隨機回復(fù):1528 字節(jié) 本字段必須包含對端發(fā)送的隨機數(shù)據(jù)字段(對 C2 來說是 S1,對 S2 來說是 C1) 内狸。 每個對等端可以用時間和時間 2 字段中的時間戳來快速地估計帶寬和延遲检眯。 但這樣做可 能并不實用。
RTMP握手的這個過程就是完成了兩件事:1. 校驗客戶端和服務(wù)器端RTMP協(xié)議版本號昆淡,2. 是發(fā)了一堆數(shù)據(jù)锰瘸,猜想應(yīng)該是測試一下網(wǎng)絡(luò)狀況,看看有沒有傳錯或者不能傳的情況瘪撇。
-
流程圖如下
在這里插入圖片描述
3.1.2. 建立網(wǎng)絡(luò)連接
-
流程圖如下:
在這里插入圖片描述
下面對建立網(wǎng)絡(luò)連接的流程簡單介紹
- 客戶端發(fā)送命令消息中的“連接”(connect)到服務(wù)器获茬,請求與一個服務(wù)應(yīng)用實例建立連接。
- 服務(wù)器接收到連接命令消息后倔既,發(fā)送確認(rèn)窗口大小(Window Acknowledgement Size)協(xié)議消息到客戶端恕曲,同時連接到連接命令中提到的應(yīng)用程序。
- 服務(wù)器發(fā)送設(shè)置帶寬()協(xié)議消息到客戶端渤涌。
- 客戶端處理設(shè)置帶寬協(xié)議消息后佩谣,發(fā)送確認(rèn)窗口大小(Window Acknowledgement Size)協(xié)議消息到服務(wù)器端。
- 服務(wù)器發(fā)送用戶控制消息中的“流開始”(Stream Begin)消息到客戶端实蓬。
- 服務(wù)器發(fā)送命令消息中的“結(jié)果”(_result)茸俭,通知客戶端連接的狀態(tài)。
注意:
這里面的connect 命令消息安皱,命令里面包含什么東西调鬓,協(xié)議中沒有說,真實通信中要指定一些編解碼的信息酌伊,這些信息是以AMF格式發(fā)送的, 其中audioCodecs和videoCodecs這兩個指定音視頻編碼信息的不能少的腾窝。
Window Acknowledgement Size 是設(shè)置接收端消息窗口大小,一般是2500000字節(jié)居砖,即告訴客戶端你在收到我設(shè)置的窗口大小的這么多數(shù)據(jù)之后給我返回一個ACK消息虹脯,告訴我你收到了這么多消息。在實際做推流的時候推流端要接收很少的服務(wù)器數(shù)據(jù)奏候,遠(yuǎn)遠(yuǎn)到達(dá)不了窗口大小循集,所以基本不用考慮這點。而對于服務(wù)器返回的ACK消息一般也不做處理蔗草,我們默認(rèn)服務(wù)器都已經(jīng)收到了這么多消息咒彤。
服務(wù)器返回的_result命令類型消息的payload length一般不會大于128字節(jié)疆柔,但是在最新的nginx-rtmp中返回的消息長度會大于128字節(jié),所以一定要做好收包镶柱,組包的工作婆硬。
3.1.3. 建立網(wǎng)絡(luò)流
- 建立網(wǎng)絡(luò)流過程簡述
創(chuàng)建完網(wǎng)絡(luò)連接之后就可以創(chuàng)建網(wǎng)絡(luò)流了
過程如下:
- 客戶端發(fā)送命令消息中releaseStream命令到服務(wù)器端
- 客戶端發(fā)送命令消息中FCPublish命令到服務(wù)器端
- 客戶端發(fā)送命令消息中的“創(chuàng)建流”(createStream)命令到服務(wù)器端。
- 服務(wù)器端接收到“創(chuàng)建流”命令后奸例,發(fā)送命令消息中的“結(jié)果”(_result),通知客戶端流的狀態(tài)向楼。
注意:解析服務(wù)器返回的消息會得到一個stream ID, 這個ID也就是以后和服務(wù)器通信的 message stream ID, 一般返回的是1查吊,不固定。
-
流程圖如下:
在這里插入圖片描述
3.1.4. 播放流
圖如下: