流媒體:RTMP 協議完全解析

前言
RTMP(Real Time Messaging Protocol) 是由 Adobe 公司基於 Flash Player 播放器對應的音視頻 flv 封裝格式提出的一種,基於TCP 的數據傳輸協議。本身具有穩定、兼容性強、高穿透的特點。常被應用於流媒體直播、點播等場景。常用於推推流方(主播)的穩定傳輸需求。

RTMP 的傳輸:消息塊 & 消息封包傳輸

RTMP 協議爲了維持穩定連續傳遞,避免單次傳輸數據量問題,採用了傳輸層封包,數據流切片的實現形式。被用來對當前帶寬進行劃分和複用的最小傳輸單位,被稱爲 Chunk 即消息塊。通常情況下,一個有效的消息,如果數據量超出當前 Chunk Size 的話,則會被拆分成多個分塊來分批傳輸。通過指定首個 Chunk 和後續 Chunk 類型,以及 Chunk Header 其他標誌性數據,來使當前被切割的消息,能夠在對端得到有效的還原和執行。我們以 MetaData 類型消息(Data AFM3 16)舉例:
在這裏插入圖片描述

例子 中用來作爲演示的 MetaData 高達 400 Bytes(不會吧?不會吧?不會吧?),而我們用 RTMP 的默認 Chunk Size 爲 128 bytes。因此,當我們使用 Message Type 爲 16 的 Data AFM3 類型的數據消息,通知對端當前元數據信息的時候,就需要切片了。

RTMP 的傳輸:消息塊的組成

想要了解 RTMP 則必須對其使用的網絡傳輸數據封裝格式有一定的瞭解。RTMP 協議是以分組形式傳送數據包。一個完整的數據塊包含兩個部分:Chunk Header 和 Chunk Data,這兩者組合在一起,構成了一個有效的消息類型,結構如下:

在這裏插入圖片描述

  • 基礎數據頭(Basic Header):保存 CS ID、Chunk Type(決定 Msg Header 類型)
  • 消息數據頭(Message Header):包含被髮送消息的相關信息,類型Chunk Type決定
  • 擴展時間戳(Extended Timestamp)(32-bits):消息頭攜帶的時間戳擴展位

基礎數據頭

基礎數據頭,Chunk stream ID 可以配置爲3~65599 這 65597 個不同標誌中的其中一種。根據持有 Chunk stream ID 的長度,RTMP 規格將基礎數據頭分爲3種:ID 在 2~63 範圍內的 1-Byte 版;ID 在 64~319 範圍內的 2-Byte 版;ID 在 64~65599 範圍內的 3-Byte 版。基礎數據頭組成,也包含三個部分。在這裏插入圖片描述

  • format message type 標誌位 fmt(2-bits):用來標誌消息類型,也被稱爲 Chunk Type
  • cs id 字段(6-bits):用來表示 63 以內的ID的標誌位,0、1兩個標記被佔用做擴展標記cs id - 64字段
  • (8 or 16-bits):用來根據擴展標誌擴充的,廣範圍標誌位

需要注意的是,Chunk stream ID 是用來區分消息信道的。因爲 RTMP 協議,所有的通信都是通過同一個 TCP 來完成的,因此所有類型的通信信道需要由 Chunk stream ID 來進行區分,從而判斷當前收到的消息所屬的信道類型。當然,這個是由用戶定義的,不做區分其實也不影響實際操作(雖然 Adobe 官方有預留,但是規範約束是一種手段,自己做雙端協定的時候也可以不按這種規範來,雖然那樣就要做一系列的配套實現了),Adobe 建議及目前市面上大部分採用如下的分類以便於操作分割:在這裏插入圖片描述
每個前後都有預留編號,以便擴充使用。

消息數據頭

消息數據頭的類型,是由基礎數據頭中的 fmt 字段來標記的。總共分爲4種類型:
在這裏插入圖片描述
其格式劃分如下:在這裏插入圖片描述
timestamp 消息時間戳(3-Bytes):標記當前消息絕對時間戳,有效位 24 bits,如果超出16777215(0xFFFFFF)則啓用擴展時間戳(Extended Timestamp)。擴展位啓用時,timestamp 位恆定爲 16777215,通過還原 32 bits 的擴展位,加合爲有效時間戳數據。時間戳在運用上對於不同消息類型會有區分,type 0 時爲絕對時間戳,type 1/2 時爲相對時間戳(時間差值)。


msg length 消息頭長度(1-Byte):攜帶 Chunk Header 數據長度信息(單位:Byte)
msg length (cont) 消息體長度(2-Bytes):攜帶 Chunk Data 數據長度信息(單位:Byte)
msg type 消息類型(1-Byte):攜帶消息類型信息,這是實際消息的類型,區別於消息頭。
ms id 字段(1-Byte):消息歸屬消息流 ID 標誌位,指定當前消息所屬信道分類
ms id (cont) 字段(3-Bytes):消息內容對應數據流 ID 標誌位,指定數據所屬數據流信道



需要闡明的是, Chunk Data 實際歸屬對應爲 ms_id (cont)。ms_id (cont) 纔是數據流對應的流標記,這個標記是我們定義的。通常服務端會需要 ms_id (cont) 來指定我們當前的數據流。以便於 RTMP 鏈接建立後,指定數據通道。進一步理解的話,cs_id、ms_id、ms_id (cont) ,這三者之間並不存在強關聯,僅僅作爲不同信息層區分使用。數據和消息頭可以毫無關聯;也可以消息頭根據c-s協定指定與引用關聯數據。

一般情況下,單用 cs_id + ms_id (cont) 就夠雙端交互和信道分割了。

那麼4種 Message Header 一般都在什麼情況使用呢?我們可以參考下表:在這裏插入圖片描述
擴展時間戳
擴展時間戳(Extended Timestamp)(32-bits)主要是配合 Message Header 內的時間戳使用,用來擴展可用時間範圍。具體見上文 Message Header 消息時間戳說明。

【免費】FFmpeg/WebRTC/RTMP音視頻流媒體高級開發
技術交流羣:【960994558】整理了一些個人覺得比較好的學習書籍、大廠面試題、有趣的項目和熱門技術教學視頻資料共享在裏面(包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等等.),有需要的可以自行添加哦!~
在這裏插入圖片描述

RTMP 的數據:可用消息類型

需要注意的是,消息往往都需要分塊發送。消息的類型只是消息本身格式的設定,和分塊傳輸的傳輸過程是不同的概念。理解上,應該把消息格式理解爲消息的信息排列樣式;把傳輸過程理解爲物理上發送數據的方案。

RTMP 消息的頭(RTMP Message Header,不是 Message Header,兩個不是同一個東西)有自己的統一格式,當然這部分也是會被切割到 Chunk 裏傳輸的。不過,因爲實際意義和 Chunk Header 內容重複,實際的實現上也可以不需要考慮這個(得約定好)。統一消息頭樣式如下:在這裏插入圖片描述
消息頭包含以下:

  • Message Type 消息類型(1-Byte):類型 ID 1 - 6 被保留用於協議控制消息。
  • Length 長度(3-Bytes):表示有效負載的字節數。以大端格式保存。
  • Timestamp 消息時間戳(4-Bytes):包含了當前消息的 timestamp。以大端格式保存。
  • Message Stream Id 消息流(3-Bytes):消息歸屬消息流 ID 標誌位。以大端格式保存。

不同的消息對應不同的數據體,應該由具體的消息來制定其中數據體所攜帶的信息。所有類型消息都被列在下表中,以便於統一講解:在這裏插入圖片描述
接下來,我們來分別看一下各大消息類型(下文圖文中的RTMP統一消息頭省略)。

協議控制類消息
協議控制類消息(Protocol Control Messages)是用來控制通信過程中,整個基礎通信配置的消息。用來負責協議雙端的通信控制。總共有 5 種類型,以大分類的形式存在於可用消息列表中,各自格式如下(整合到一張圖裏了,注意區別):在這裏插入圖片描述
chunk size 數據包最大可接受長度(31-bits):取值範圍 1~2147483647 (0x7FFFFFFF) ,但因爲消息總長度取值上限爲16777215 (0xFFFFFF) ,因此不能超過該值(單位:Bytes)
chunk stream id 截停的信道 ID(4-Bytes):Abort 消息想要停止的信道ID
sequence number 已接收的數據長度(4-Bytes):發送後,對應長度的本地數據將會被標爲已應答(Acked)(單位:Bytes)
Ack window size 應答窗口大小(4-Bytes):設置的應答窗口大小(單位:Bytes)
Limit Type 帶寬配置模式(1-Byte):帶寬配置模式有三種如下:在這裏插入圖片描述
用戶控制消息
用戶控制消息(User Control Messages)被用來實時通知對端,以進行一些相關操作。其通用格式如下:在這裏插入圖片描述
這些操作大多都是一些狀態通知類型消息,或者網絡狀態測量類型的消息。官方在自己的開源實現裏,定義了7種:在這裏插入圖片描述
如有額外的操作協定,也可以在當前官方類型之外,自行定義其他類型。不過這樣的話就需要雙端都去做配套實現了。官方源代碼這一塊兒可以參考:









https://repo.or.cz/w/rtmpdump.git/blob/8880d1456b282ee79979adbe7b6a6eb8ad371081:/librtmp/rtmp.c#l2787

命令消息
命令消息(Command Messages)是用於 C-S 進行直接交互應答的一類消息。一般情況下,命令消息的發送對端,是需要對端進行應答信號反饋的。反饋消息規定以

[ 命令消息 ] + _result 或者 [ 命令消息 ] + _error

的形式由接收命令的一方(receiver),將結果信息發送回命令的發送方(sender)。以完成一次有效的命令操作。命令構成相對簡單,其中攜帶的複雜數據則通過AMF編碼的形式,存放在命令的消息體中。

一般情況下,命令消息的消息體的通用描述內容有,name 僅用做稱謂:

在這裏插入圖片描述
數據消息
客戶端或者服務器端通過發送這些消息以發送元數據或者任何用戶數據到對端。元數據包括數據 (音頻,視頻等等) 的詳細信息,比如創建時間,時長,主題等等。這些消息可以進行 AMF0 編碼生成消息類型 18 的 Data AMF0,也可以進行 AMF3 編碼生成消息類型 15 的 Data AMF3。

一般情況下,數據消息的消息體的通用描述內容有,name 僅用做稱謂:在這裏插入圖片描述
音視頻消息
音視頻消息在結構上是一致的,本質都是純粹的數據傳輸用途,即直接將音視頻 Bytes 數據分塊發送即可。沒有多餘的信息數據。

共享對象消息
共享對象消息是持有 Flash 對象,一種爲了在多端多實例保持同步而設計的名-值對的集合對象,的消息類型。消息可以進行 AMF0 編碼生成消息類型 19 的 Shared Object Message AMF0,也可以進行 AMF3 編碼生成消息類型 16 的 Shared Object Message AMF3。每個共享消息對象,都可以包含有多個不同的共享事件。其通用格式如下:在這裏插入圖片描述
統一描述信息主要攜帶共享對象名、版本和標記數據,共享事件名-值對集合則攜帶對應共享對象的一系列指定操作。共享對象消息的可用共享事件類型有:在這裏插入圖片描述
因爲共享對象消息是基於 Flash 類型的消息,在 Adobe 停止Flash 的支持後,在現在的音視頻處理中,不太經常使用此類共享消息事件(也因爲沒有太多重要的大同步操作)。


整合消息
整合消息(Aggregate Message)被用來以消息包的形式來發送一系列上文中消息類型的集成表單。相當於用一個大的消息,將一些經過可回溯標記標示後的消息,按照指定的順序編排後發出。整合消息對應的 ms id 將會覆蓋持有子消息的 ms id(官方建議)。在這裏插入圖片描述
統計消息裏的 timestamp 和第一個子消息的 timestamp 的不同點在於子消息的 timestamp 被相對流時間標調整了偏移。每個子消息的 timestamp 都被加入偏移,以達到一個統一時間流。第一個子消息的 timestamp 應該和統計消息的 timestamp 一樣,所以這個偏移量應該爲 0。

Back Pointer 反向指針包含有前一個消息的大小 (包含前一個消息的頭)。這樣子匹配了 FLV 文件的格式,用於反向查找。

使用統計消息具有以下性能優勢:

塊流可以在一個塊中以至多一個單一完整的消息發送。因此,增加塊大小並使用統計消息減少了發送塊的數量。
子消息可以在內存中連續存儲。在網絡中系統調用發送這些數據時更高效。

四、總結

至此,RTMP 基本梳理完畢。這些規格實際上都可以自行改動,但是需要雙端一致。本質上,這套 RTMP 規範更多的是 Adobe 根據 Flash 的一系列特性制定的。所以使用中,還是會涉及到一些定製化的事情。不過,也可以直接使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章