RTMP協議相關知識

孔雨露發表於2020-04-06

@TOC

1. RTMP協議簡介

RTMP協議是一個網際網路TCP/IP五層體系結構中應用層的協議。RTMP協議中基本的資料單元稱為訊息(Message)。當RTMP協議在網際網路中傳輸資料的時候,訊息會被拆分成更小的單元,稱為訊息塊(Chunk)。RTMP 是目前主流的流媒體傳輸協議,廣泛用於直播領域,可以說市面上絕大多數的直播產品都採用了這個協議。 RTMP協議就像一個用來裝資料包的容器,這些資料可以是AMF格式的資料,也可以是FLV中的視/音訊資料。一個單一的連線可以通過不同的通道傳輸多路網路流。這些通道中的包都是按照固定大小的包傳輸的。

  • 優點
  1. CDN 支援良好,主流的 CDN 廠商都支援
  2. 協議簡單,在各平臺上實現容易
  • 缺點
  1. 基於 TCP ,傳輸成本高,在弱網環境丟包率高的情況下問題顯著
  2. 不支援瀏覽器推送
  3. 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 。客戶端可以選擇減到版本 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. 建立網路連線

  • 流程圖如下:
    在這裡插入圖片描述

下面對建立網路連線的流程簡單介紹

  1. 客戶端傳送命令訊息中的“連線”(connect)到伺服器,請求與一個服務應用例項建立連線。
  2. 伺服器接收到連線命令訊息後,傳送確認視窗大小(Window Acknowledgement Size)協議訊息到客戶端,同時連線到連線命令中提到的應用程式。
  3. 伺服器傳送設定頻寬()協議訊息到客戶端。
  4. 客戶端處理設定頻寬協議訊息後,傳送確認視窗大小(Window Acknowledgement Size)協議訊息到伺服器端。
  5. 伺服器傳送使用者控制訊息中的“流開始”(Stream Begin)訊息到客戶端。
  6. 伺服器傳送命令訊息中的“結果”(_result),通知客戶端連線的狀態。

注意

這裡面的connect 命令訊息,命令裡面包含什麼東西,協議中沒有說,真實通訊中要指定一些編解碼的資訊,這些資訊是以AMF格式傳送的, 其中audioCodecs和videoCodecs這兩個指定音視訊編碼資訊的不能少的。 Window Acknowledgement Size 是設定接收端訊息視窗大小,一般是2500000位元組,即告訴客戶端你在收到我設定的視窗大小的這麼多資料之後給我返回一個ACK訊息,告訴我你收到了這麼多訊息。在實際做推流的時候推流端要接收很少的伺服器資料,遠遠到達不了視窗大小,所以基本不用考慮這點。而對於伺服器返回的ACK訊息一般也不做處理,我們預設伺服器都已經收到了這麼多訊息。 伺服器返回的_result命令型別訊息的payload length一般不會大於128位元組,但是在最新的nginx-rtmp中返回的訊息長度會大於128位元組,所以一定要做好收包,組包的工作。

3.1.3. 建立網路流

  • 建立網路流過程簡述 建立完網路連線之後就可以建立網路流了

過程如下

  1. 客戶端傳送命令訊息中releaseStream命令到伺服器端
  2. 客戶端傳送命令訊息中FCPublish命令到伺服器端
  3. 客戶端傳送命令訊息中的“建立流”(createStream)命令到伺服器端。
  4. 伺服器端接收到“建立流”命令後,傳送命令訊息中的“結果”(_result),通知客戶端流的狀態。

注意:解析伺服器返回的訊息會得到一個stream ID, 這個ID也就是以後和伺服器通訊的 message stream ID, 一般返回的是1,不固定。

  • 流程圖如下:
    在這裡插入圖片描述

3.1.4. 播放流

圖如下:

在這裡插入圖片描述
5.