RTMP協定學習——從握手到播放

2023-11-11 06:00:43

從使用者端發起播放請求,到rtrmp視訊流開始播放,大致經過了握手->建立連線->建立流->播放這幾步比較重要的步驟。下面我將結合wireshark的抓包,對其中的每個流程進行分析和學習。

握手

RTMP協定基於TCP,TCP建立連線有三次握手。在TCP連線建立以後,會再進行一次RTMP協定層次的握手。

TCP握手

TCP建立連線的三次握手如圖所示:

從wireshark的抓包中,也可以看到TCP的三次握手。

RTMP握手

在TCP建立連線成功後,rtmp會再進行三次握手。

可以通過在wireshark的過濾器中輸入rtmpt來過濾RTMP協定的資料,如圖所示:


使用者端首先傳送C0、C1到伺服器。其中C1的大小是固定的,為1536個位元組。伺服器端收到C0,C1後,傳送S0,S1給使用者端,S1的大小和C1一樣,為1536個位元組。伺服器端收齊C0,C1後,傳送S2給使用者端,使用者端收齊S0,S1後,傳送C2給伺服器端。C2和S2的大小也為1536個位元組。可以看出來,rtmp協定握手交換資料包文的大小是固定的。
下圖是引自wiki,可以很清晰的看到使用者端和伺服器端的互動流程和包之間的關係:

C0 and S0

C0和S0都是單一的八位位元組,裡面包含著使用者端要求的RTMP版本號



C1 and S1

C1和S1的長度都是1536個位元組,裡面包含著時間戳,4個位元組的0和1528個位元組隨機生成的資料


C2 and S2

C2和S2的長度是1536個位元組,其中的資料包括:

  • time:終端在 S1 (給 C2) 或者 C1 (給 S2) 發的 timestamp。
  • time2:終端先前發出封包 (S1 或者 C1) timestamp。
  • random echo:終端發的 S1 (給 C2) 或者 S2 (給 C1) 的亂資料。



connect

使用者端

握手完成後,使用者端會傳送connect命令到伺服器端,請求連線一個伺服器應用的範例。
connect訊息組成如下:

其中Command Object包含了多個欄位,來幫助使用者端和伺服器端的連線。
Command Object包含的欄位如下:

  • app:使用者端連線到伺服器應用端的名字
  • flashver:Flash Player 版本號。和ApplicationScript getversion() 方法返回的是同一個字串。
  • swfUrl:進行當前連線的 SWF 檔案源地址。
  • tcUrl:伺服器 URL。
  • fpad:代理標誌,如果使用了代理就是 true。
  • audioCodecs:表明使用者端所支援的音訊編碼。
  • videoCodecs:表明支援的視訊編碼。
  • videoFunction:表明所支援的特殊視訊方法。
  • pageUrl:SWF 檔案所載入的網頁 URL。
  • objectEncoding:AMF 編碼方法。

總的來說,就是包含了版本,流名稱,url,音視訊編碼等後面播放視訊流時所用的資訊,以保證後續伺服器端傳送的視訊資料可以正常播放。
wireshark抓包如下:

伺服器端

伺服器端收到connect命令後,傳送了幾條訊息給使用者端。如下圖所示:

Window Acknowledgement size

Window Acknowledgement size的結構如下:

Window Acknowledgement size是伺服器端用來通知對端傳送和應答之間視窗大小的。
此例子中傳送的視窗大小為2500000。

Set Peer Bandwidth

該訊息裡限制對端輸出頻寬。

限制型別取以下值之一:

  • 0 - Hard:對端應該限制其輸出頻寬到指示的視窗大小。

  • 1 - Soft:對端應該限制其輸出頻寬到知識的視窗大小,或者已經有限制在其作用的話就取兩者之間的較小值。

  • 2 - Dynamic:如果先前的限制型別為 Hard,處理這個訊息就好像它被標記為 Hard,否則的話忽略這個訊息。

Set Chunk Size

設定塊大小,以通知對端新的最大塊大小,預設為128位元組。

Chunk是訊息的一段。訊息在網路傳送之前被拆分成很多小的部分。Chunk可以確保端到端交付所有訊息有序 timestamp,即使有很多不同的流。Chunk的格式如下:

createStream

在connect完成後就可以建立或者存取RTMP流了。

從抓包資料可以看到,伺服器端向用戶端傳送了三條命令,分別是Window Acknowledgement sizecreateStream和_checkbw

  • Window Acknowledgement size是對之前伺服器端傳送的Set Peer Bandwidth訊息的應答。
  • _checkbw用於檢查頻寬,來評估與伺服器的連線質量和頻寬。

主要的訊息是creatStream,使用者端傳送此訊息到伺服器端,建立一個邏輯通道,用於訊息通訊。音訊、視訊、後設資料均通過createStream建立的資料通道進行互動。createStream訊息的結構如下:

使用者端傳送createStream請求之後,伺服器端會反饋一個結果給使用者端,如果成功,則返回_result,如果失敗,則返回_error。
wireshark抓包資料如下:

在伺服器端返回的資料中,最後一個欄位為Stream ID,作為該Stream的唯一標識。此例子中返回的是1,後續的視訊或音訊的Stream ID就是1。
至此,使用者端和伺服器端的通道已經建立完成,接下來就可以進行資料的傳輸了。

play

createStream成功後,使用者端傳送play命令,通知伺服器端開始播放視訊流。
play訊息有7個欄位,其中最後3個為可選欄位。

  • Command Name: String型別,命令的名稱,為「play」。
  • Transaction ID: Number型別,事務ID。
  • Command Object:命令資訊不存在。設為 null 型別。
  • Stream Name: String型別,要播放流的名字。
  • Start(可選): Number型別,以秒為單位定義開始時間,預設值為 -2,表示使用者首先嚐試播放流名欄位中定義的直播流。
  • Duration(可選): Number型別,以秒為單位定義了回放的持續時間。預設值為 -1。-1 值意味著一個直播流會一直播放直到它不再可用或者一個錄製流一直播放直到結束。
  • Reset(可選): Boolean型別,定義了是否對以前的播放列表進行 flush。
    wireshark抓包資料為:

從抓包資料可以看到,此次事務ID為5;流的名稱為「test」;start為-2000ms,即-2s,使用者首先嚐試播放流名欄位中定義的直播流。

使用者端傳送play命令來播放指定流後,就開始傳輸音視訊資料。後面就是使用者端按照流程對接收到的資料進行解協定,解封裝,解碼..........
........此處省略一萬字...........