RTMP(Real Time Messaging Protocol 實時消息傳輸協議)
Message 結構:

- Message Type:它是一個消息類型的ID,通過該ID接收方可以判斷接收到的數據的類型,從而做相應的處理。
- Message Type ID在1-7的消息用於協議控制,這些消息一般是RTMP協議自身管理要使用的消息,用戶一般情況下無需操作其中的數據。
- Message Type ID為8,9的消息分別用於傳輸音頻和視頻數據。
- Message Type ID為15-20的消息用於發送AMF編碼的命令,負責用戶與服務器之間的交互,比如播放,暫停等。
- Playload Length: 消息負載的長度,即音視頻相關信息的的數據長度,4個字節
- TimeStamp:時間戳,3個字節。
- Stream ID:消息的唯一標識。拆分消息成Chunk時添加該ID,從而在還原時根據該ID識別Chunk屬於哪個消息。
- Message Body:消息體,承載了音視頻等信息。
Chunk 結構:
- Basic Header:基本的頭部信息,在頭部信息里面包含了chunk stream ID(流通道Id,用來標識指定的通道)和chunk type(chunk的類型)。
- Message Header:消息的頭部信息,包含了要發送的實際信息(可能是完整的,也可能是一部分)的描述信息。Message Header的格式和長度取決於Basic Header的chunk type。
- Extended TimeStamp:擴展時間戳。
- Chunk Data:塊數據。
RTMP在傳輸數據的時候,發送端會把需要傳輸的媒體數據封裝成消息,然后把消息拆分成消息塊,再一個一個進行傳輸。
接收端收到消息塊后,根據Message Stream ID重新將消息塊進行組裝、組合成消息,再解除該消息的封裝處理就可以還原出媒體數據。
由此可以看出,RTMP收發數據是以Chunk為單位,而不是以Message為單位。需要注意的是,RTMP發送Chunk必須是一個一個發送,后面的Chunk必須等前面的Chunk發送完成。
工作流程:
- 握手(RTMP連接都是以握手作為開始)
- 建立連接 (建立客戶端與服務器之間的“網絡連接”)
- 建立流 (建立客戶端與服務器之間的“網絡流”)
- 推流 (傳輸視音頻數據)
1. 握手
在RTMP連接建立后,服務端與客戶端需要通過3次交換報文完成握手,握手與其他的協議不同,是由三個靜態大小的塊,而不是可變大小的塊組成的,客戶端與服務器發送相同的三個chunk:
- 客戶端發送C0,C1,C2
- 服務端發送S0,S1,S2
其數據格式如下:
C0、S0:
長度是一個字節,這個字段表示服務器選擇的 RTMP 版本。
- RTMP1.0規范所定義的版本是 3;
- 0-2 是早期產品所用的,已被丟棄;
- 4-31保留在未來使用;
- 32-255 不允許使用(為了區分其他以某一字符開始的文本協議)。
如果服務無法識別客戶端請求的版本,應該返回 3 。客戶端可以選擇減到版本 3 或選擇取消握手。
C1、S1:
- 時間:4 字節 本字段包含時間戳。該時間戳應該是發送這個數據塊的端點的后續塊的時間起始點。可以是 0,* 或其他的 任何值。為了同步多個流,端點可能發送其塊流的當前值。
- 零:4 字節 本字段必須是全零。
- 隨機數據:1528 字節。 本字段可以包含任何值。 因為每個端點必須用自己初始化的握手和對端初始化的握手來區分身份,所以這個數據應有充分的隨機性。但是並不需要加密安全的隨機值,或動態值。
C2、S2:
- 時間:4 字節 本字段必須包含對等段發送的時間(對 C2 來說是 S1,對 S2 來說是 C1)。
- 時間2:4 字節 本字段必須包含先前發送的並被對端讀取的包的時間戳。
- 隨機回復:1528 字節本字段必須包含對端發送的隨機數據字段(對 C2 來說是 S1,對 S2 來說是 C1)。每個對等端可以用時間和時間2字段中的時間戳來快速地估計帶寬和延遲。 但這樣做可能並不實用。
其發送規則如下:
- 客戶端發送 C0,C1 塊,握手開始。
- 客戶端在發送 C2 之前客戶端必須等待接收 S1 。
- 客戶端在發送任何數據之前客戶端必須等待接收 S2。
- 服務端在發送 S0 和 S1 之前必須等待接收 C0,也可以等待接收 C1。
- 服務端在發送 S2 之前必須等待接收 C1。
- 服務端在發送任何數據之前必須等待接收 C2。
RTMP握手的這個過程完成了兩件事:
- 校驗客戶端和服務器端RTMP協議版本號
- 發了一堆數據,猜想應該是測試一下網絡狀況,看看有沒有傳錯或者不能傳的情況
2. 建立連接
- 客戶端發送命令消息中的“連接”(connect)到服務器,請求與一個服務應用實例建立連接。
- 服務器接收到連接命令消息后,發送確認窗口大小(Window Acknowledgement Size)協議消息到客戶端,同時連接到連接命令中提到的應用程序。
- 服務器發送設置帶寬協議消息到客戶端。
- 客戶端處理設置帶寬協議消息后,發送確認窗口大小(Window Acknowledgement Size)協議消息到服務器端。
- 服務器發送用戶控制消息中的“流開始”(Stream Begin)消息到客戶端。
- 服務器發送命令消息中的“結果”(_result),通知客戶端連接的狀態。
注意:
- 這里面的connect 命令消息,命令里面包含什么東西,協議中沒有說,真實通信中要指定一些編解碼的信息,這些信息是以AMF格式發送的, 其中audioCodecs和videoCodecs這兩個指定音視頻編碼信息的不能少的。
- Window Acknowledgement Size 是設置接收端消息窗口大小,一般是2500000字節,即告訴客戶端你在收到我設置的窗口大小的這么多數據之后給我返回一個ACK消息,告訴我你收到了這么多消息。在實際做推流的時候推流端要接收很少的服務器數據,遠遠到達不了窗口大小,所以基本不用考慮這點。而對於服務器返回的ACK消息一般也不做處理,我們默認服務器都已經收到了這么多消息。
- 服務器返回的_result命令類型消息的payload length一般不會大於128字節,但是在最新的nginx-rtmp中返回的消息長度會大於128字節,所以一定要做好收包,組包的工作。
3. 建立流
- 客戶端發送命令消息中 releaseStream 命令到服務器端
- 客戶端發送命令消息中 FCPublish 命令到服務器端
- 客戶端發送命令消息中的“創建流”(createStream)命令到服務器端。
- 服務器端接收到“創建流”命令后,發送命令消息中的“結果”(_result),通知客戶端流的狀態。
- 解析服務器返回的消息會得到一個stream ID, 這個ID也就是以后和服務器通信的 message stream ID, 一般返回的是1,不固定。
4. 推流
推流准備工作的最后一步是 Publish Stream,即向服務器發一個publish命令,這個命令的message stream ID 就是上面 create stream 之后服務器返回的stream ID,發完這個命令一般不用等待服務器返回的回應,直接下一步發送音視頻數據。
有些rtmp庫還會發setMetaData消息,這個消息可以發也可以不發,里面包含了一些音視頻編碼的信息。