@TOC
1. RTMP協議簡介
RTMP協議是一個互聯網TCP/IP五層體系結構中應用層的協議。RTMP協議中基本的數據單元稱為消息(Message)。當RTMP協議在互聯網中傳輸數據的時候,消息會被拆分成更小的單元,稱為消息塊(Chunk)。RTMP 是目前主流的流媒體傳輸協議,廣泛用于直播領域,可以說市面上絕大多數的直播產品都采用了這個協議。
RTMP協議就像一個用來裝數據包的容器,這些數據可以是AMF格式的數據,也可以是FLV中的視/音頻數據。一個單一的連接可以通過不同的通道傳輸多路網絡流。這些通道中的包都是按照固定大小的包傳輸的。
- 優點
- CDN 支持良好,主流的 CDN 廠商都支持
- 協議簡單,在各平臺上實現容易
- 缺點
- 基于 TCP ,傳輸成本高,在弱網環境丟包率高的情況下問題顯著
- 不支持瀏覽器推送
- Adobe 私有協議,Adobe 已經不再更新
2. RTMP協議基本概念
2.1 消息相關概念
RTMP傳輸的數據的基本單元為Message,但是實際上傳輸的最小單元是Chunk(消息塊),因為RTMP協議為了提升傳輸速度,在傳輸數據的時候,會把Message拆分開來,形成更小的塊,這些塊就是Chunk。
2.1.1 消息
消息是RTMP協議中基本的數據單元。不同種類的消息包含不同的Message Type ID,代表不同的功能。RTMP協議中一共規定了十多種消息類型,分別發揮著不同的作用。
1-7的消息用于協議控制,這些消息一般是RTMP協議自身管理要使用的消息,用戶一般情況下無需操作其中的數據
Message Type ID為8,9的消息分別用于傳輸音頻和視頻數據
Message Type ID為15-20的消息用于發送AMF編碼的命令,負責用戶與服務器之間的交互,比如播放,暫停等等
消息首部(Message Header)有四部分組成:標志消息類型的Message Type ID,標志消息長度的Payload Length,標識時間戳的Timestamp,標識消息所屬媒體流的Stream ID
下面針對上圖的消息結構體進行簡要分析:
- 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:消息體,承載了音視頻等信息。
2.1.2. 消息塊
在網絡上傳輸數據時,消息需要被拆分成較小的數據塊,才適合在相應的網絡環境上傳輸。RTMP協議中規定,消息在網絡上傳輸時被拆分成消息塊(Chunk)。
消息塊首部(Chunk Header)有三部分組成:
- 用于標識本塊的Chunk Basic Header
- 用于標識本塊負載所屬消息的Chunk Message Header
- 以及當時間戳溢出時才出現的Extended Timestamp
通過上圖可以看出,消息塊在結構上與與消息類似,有Header和Body。
下面對圖的每個部分簡要介紹:
- 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發送完成。
2.1.3. 消息分塊
在消息被分割成幾個消息塊的過程中,消息負載部分(Message Body)被分割成大小固定的數據塊(默認是128字節,最后一個數據塊可以小于該固定長度),并在其首部加上消息塊首部(Chunk Header),就組成了相應的消息塊。消息分塊過程如圖5所示,一個大小為307字節的消息被分割成128字節的消息塊(除了最后一個)。
RTMP傳輸媒體數據的過程中,發送端首先把媒體數據封裝成消息,然后把消息分割成消息塊,最后將分割后的消息塊通過TCP協議發送出去。接收端在通過TCP協議收到數據后,首先把消息塊重新組合成消息,然后通過對消息進行解封裝處理就可以恢復出媒體數據。
2.2. RTMP中的邏輯結構
RTMP協議規定,播放一個流媒體有兩個前提步驟
第一步,建立一個網絡連接(NetConnection)
第二步,建立一個網絡流(NetStream)。
其中,網絡連接代表服務器端應用程序和客戶端之間基礎的連通關系。網絡流代表了發送多媒體數據的通道。服務器和客戶端之間只能建立一個網絡連接,但是基于該連接可以創建很多網絡流。他們的關系如圖所示:
2.5.
2.6.
2.7.
3. RTMP協議流程
總的流程圖如下:
3.1. 連接流程
播放一個RTMP協議的流媒體需要經過以下幾個步驟:
- 握手
- 建立連接
- 建立流
- 播放
RTMP連接都是以握手作為開始的。建立連接階段用于建立客戶端與服務器之間的“網絡連接”;建立流階段用于建立客戶端與服務器之間的“網絡流”;播放階段用于傳輸視音頻數據。
下面來詳細分析一下這幾個過程都做了一些什么東東
3.1.1.握手
在rtmp連接建立后,服務端與客戶端需要通過3次交換報文完成握手,握手其他的協議不同,是由三個靜態大小的塊,而不是可變大小的塊組成的,客戶端與服務器發送相同的三個chunk,客戶端發送c0,c1,c2,服務端發送s0,s1,s2。
- 發送規則
握手開始于客戶端發送 C0,C1 塊。
在發送 C2 之前客戶端必須等待接收 S1 。
在發送任何數據之前客戶端必須等待接收 S2。
服務端在發送 S0 和 S1 之前必須等待接收 C0,也可以等待接收 C1。
服務端在發送 S2 之前必須等待接收 C1。
服務端在發送任何數據之前必須等待接收 C2。
- 數據格式
C0與S0
C0和S0的長度是一個字節,在 S0 中這個字段表示服務器選擇的 RTMP 版本。rtmp1.0規范所定義的版本是 3;0-2 是早期產品所用的,已被丟棄;4-31保留在未來使用;32-255 不允許使用(為了區分其他以某一字符開始的文本協議)。如果服務無法識別客戶端請求的版本,應該返回 3 ??蛻舳丝梢赃x擇減到版本 3 或選擇取消握手。
C1與S1
C1 和 S1 有 1536 字節長,由下列字段組成:
時間:4 字節 本字段包含時間戳。該時間戳應該是發送這個數據塊的端點的后續塊的時間起始點。可以是 0,* 或其他的 任何值。為了同步多個流,端點可能發送其塊流的當前值。
零:4 字節 本字段必須是全零。
隨機數據:1528 字節。 本字段可以包含任何值。 因為每個端點必須用自己初始化的握手和對端初始化的握 手來區分身份,所以這個數據應有充分的隨機性。但是并不需要加密安全的隨機值,或者動態值
C2與S2
C2 和 S2 消息有 1536 字節長。只是 S1 和 C1 的回復。本消息由下列字段組成。
時間:4 字節 本字段必須包含對等段發送的時間(對 C2 來說是 S1,對 S2 來說是 C1)。
時間 2:4 字節 本字段必須包含先前發送的并被對端讀取的包的時間戳。
隨機回復:1528 字節 本字段必須包含對端發送的隨機數據字段(對 C2 來說是 S1,對 S2 來說是 C1) 。 每個對等端可以用時間和時間 2 字段中的時間戳來快速地估計帶寬和延遲。 但這樣做可 能并不實用。
RTMP握手的這個過程就是完成了兩件事:1. 校驗客戶端和服務器端RTMP協議版本號,2. 是發了一堆數據,猜想應該是測試一下網絡狀況,看看有沒有傳錯或者不能傳的情況。
-
流程圖如下
在這里插入圖片描述
3.1.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.1.3. 建立網絡流
- 建立網絡流過程簡述
創建完網絡連接之后就可以創建網絡流了
過程如下:
- 客戶端發送命令消息中releaseStream命令到服務器端
- 客戶端發送命令消息中FCPublish命令到服務器端
- 客戶端發送命令消息中的“創建流”(createStream)命令到服務器端。
- 服務器端接收到“創建流”命令后,發送命令消息中的“結果”(_result),通知客戶端流的狀態。
注意:解析服務器返回的消息會得到一個stream ID, 這個ID也就是以后和服務器通信的 message stream ID, 一般返回的是1,不固定。
-
流程圖如下:
在這里插入圖片描述
3.1.4. 播放流
圖如下: