搭建netstream伺服器
㈠ rtmp規范1.0全面指南
RTMP(real time messaging protocol)協議
本文為Adobe rtmp規范1.0的中文介紹,其中內容大部分都是翻譯自rtmp官方文檔 rtmp_specification_1.0.pdf
Adobe的實時消息傳輸協議( RTMP )通過可靠的流傳輸(如 TCP [RFC0793] )提供雙向消息多路傳輸服務,用於在端到端之間傳輸帶有時序信息的視頻,音頻和數據消息的並行流。 穿過多層流, RTMP 消息塊流不提供任何控制的優先順序別和相似形式,但是可以用於高層協議提供這樣的優先順序,例如:一段實時視頻服務會選擇丟棄給緩慢的客戶的視頻信息確保音頻信息可以及時被接收。 RTMP消息塊流 包含它自己的入隊協議控制消息,也提供一個高層協議機制用於嵌入用戶的控制消息。
有效負載:Payload
包含在包中的數據,就像音頻樣本或者壓縮的視頻數據。
包:Packet
一個數據包由固定的包頭和有效負載數據組成,一些底層協議或許需要包的封裝來被定義。
埠:Port
在 TCP/IP 協議中定義的用正整數表示的埠號用於在傳輸中提取以區分目標主機的不同應用,用於 OSI 傳輸層的傳輸選擇( TSEL )就是埠。
傳輸地址:Transport address
網路地址和埠的組合識別一個傳輸層終端埠,例如一個IP地址和TCP埠,數據包從一個源傳輸層地址傳送到目標段的傳輸層地址。
消息流:Message stream
一個通信的邏輯通道,允許消息流通。
消息流ID:Message stream ID
每一個消息擁有一個分配的ID識別跟隨的消息流。
消息塊:Chunk
消息的片段,消息被分成小的部分,在他們在網路中發送之前交叉存儲。消息塊確保定製時間戳的端到端全消息傳送,穿過多層流。
消息塊流:Chunk stream
一個通信的邏輯通道,允許消息塊在一個特定的方向上流通,消息塊流可以從客戶端傳送到伺服器,也可以相反。
消息塊流ID:Chunk stream ID
每一個消息塊有一個分配的ID用於識別更隨的消息塊流。
復合技術:Multiplexing
把分開的音視頻數據組合成一條音視頻流的過程,使同時傳送許多音視頻數據成為可能。
逆復合技術:DeMultiplexing
復合的反向過程,交叉存取組裝的音頻視頻數據,使他們成為最初的音視頻數據
遠程過程調用:Remote Procere Call (RPC)
允許客戶端或伺服器在對等端調用子常式或過程的請求。
Action Message Format (AMF)
一種緊湊的二進制格式,用於序列化 ActionScript object graphs 。 可以透過 AMF overHTTP 的方式將 flash 端資料編碼後傳回server,server端的 remoting adaptor 接收到資料後則會解碼回正確的 native 對象,交給正確的程序處理。
所有的整數欄位都被引入到了位元組順序當中,位元組0是第一個顯示出來的,也是一個詞和一個欄位中最重要的。這種順序就是通常所說的「大端」。如果沒有特殊說明,在本文檔中數字常量都是用十進製表示。
除另有規定外, RTMP 中的所有數據都是位元組對齊的。例如,一個16位欄位可能處於奇數位元組偏移處。 在指定填充的地方,填充位元組應該是0。
RTMP 中的時間戳相對於未指定的時期是以整數毫秒為單位給出的。 通常,每個流將以時間戳0開始,但這不是必需的,只要兩個終端在時間點上達成一致。 請注意,這意味著跨多個流(尤其是來自不同主機)的任何同步都需要一些 RTMP 外的其他機制。
時間戳必須始終在線性的增加,允許應用程序處理非同步傳輸,帶寬度量,檢測,和流控制。
由於時間戳長度為32位,因此它們每隔49天,17小時,2分鍾,47.296秒滾動一次。 由於流可以連續運行,可能持續數年, RTMP 應用程序應該在處理時間戳時使用序列號演算法 [RFC1982] ,並且應該能夠處理回繞。 例如,假定所有相鄰的時間戳都在 2^31 - 1 毫秒之間,所以10000會在4000000000之後,而3000000000會在4000000000之前。
時間戳增量delta也被指定為相對於先前時間戳的無符號整數毫秒數。 時間戳增量delta可以是24位或32位。
本節介紹實時消息傳送協議塊流( RTMP塊流 )。 它為更高級別的多媒體流協議提供復用和打包服務。 雖然 RTMP Chunk Stream 旨在與實時消息傳送協議配合使用,但它可以處理發送消息流的任何協議。 每條消息都包含時間戳和有效負載類型標識。 RTMP Chunk Stream 和 RTMP 一起適用於各種音頻 - 視頻應用,從一對一和一對多實時廣播到視頻點播服務,再到互動式會議應用。
當與可靠的傳輸協議(如 TCP [RFC0793] )一起使用時, RTMP塊流 提供了保證所有消息在多個流中按時間排序的端到端傳送。 RTMP塊流 不提供任何優先順序或類似的控制形式,但可以由更高級別的協議提供這種優先順序。
可以拆分成塊以支持復用的消息格式取決於更高級別的協議。 但是,消息格式應該包含下列創建塊所必需的欄位。
時間戳:
消息的時間戳,這個欄位可以傳輸4個位元組。
長度:
消息的有效負載的長度,如果消息頭不能被省略,它應該包含在長度中,這個欄位在消息塊包頭中佔有3個位元組。
類型ID:
協議控制消息的類型欄位的范圍是被保留的,這些傳播信息的消息由 RTMP消息塊 和高層協議處理,所有其他的類型ID可被高層協議使用,對 RTMP消息塊 來說當做不透明的值,實際上, RTMP Chunk Stream 中的任何內容都不需要將這些值用作類型; 所有(非協議)消息可以是相同類型的,或者應用程序可以使用類型id來區分同步蹤跡而不是類型。 該欄位佔用塊頭中的1個位元組。
消息流ID:
消息流ID可以是任意的值。 復合到相同塊流上的不同消息流可以基於它們的消息流ID進行逆復合操作。 除此之外,就 RTMP 塊流而言,這是一個不透明的值。 該欄位以小尾數格式佔用塊頭中的4個位元組。
RTMP 連接始於握手。 rtmp 握手與其他協議的握手不同; 它由三個相同大小的塊組成,而不是由可變大小的塊組成。
客戶端(連接已初始化的終端)和伺服器都發送相同的三個塊。 為了說明,由客戶端發送的3個塊分別為 C0 , C1 , C2 ,由服務端發送的3個塊分別為 S0 , S1 , S2 。
握手以客戶端發送 C0 和 C1 消息塊位開始,客戶端必須等到 S1 到達在發送 C2 。客戶端必須等到 S2 接收到才可以發送其他的數據;服務端必須等到 C0 到達才發送 S0 和 S1 ,在 C1 之後也會等待。服務端必須等到 C1 到達才發送 S2 ,服務端必須等到 C2 到達後才發送其他數據。
C0 和 S0 都是單個8位位元組,可以看成一個8位整形欄位。
8比特版本:在C0中,這個欄位識別客戶端需求的RTMP的版本,在S0中,這個欄位識別伺服器端選擇的RTMP的版本,被定義的是版本3,0到2是早前的版本使用的,4到31保留用於未來使用,32到255還沒有被允許。不能區分客戶的請求的版本的服務應該以3返回,客戶端可以選擇降級到版本3,或放棄握手。
C1 和 S1 包長度為1536個8位位元組,包含以下欄位:
time(4個位元組) :這個欄位包含時間戳,被當做後續消息塊從終端發送的時間點,也許是0,或者一些任意的值。為了同步多路消息塊流,終端或許希望發送其他消息塊流的時間戳的當前值。
zero(4各個位元組) :這個欄位必須全0。
random data(1528個位元組) :這個欄位可以包含任何任意的值,因為每個終端必須區分自己初始化的握手的返回數據和對方初始化的握手的返回數據,這個數據應該發送一些隨機數。但是沒有必要用密碼保護隨機數和動態值。
C2 和 S2 包長度為1536個8位位元組,分別類似於 S1 和 C1 的原樣返回,由一下幾個欄位組成:
time(4個位元組) :
這個欄位必須包含由對端發送的 S1 (對應 C2 )或者 C1 (對應 S2 )的時間戳.
time2(4個位元組) :
這個欄位必須包含先前的由對端發送的數據包( S1 或者 C1 )被讀取的時間戳。
random echo(1528個位元組) :
這個欄位必須包含在對端發送的 S1 (對應 C2 )或 S2 (對應 C1 )數據包中的隨機數據欄位。 任何一方都可以使用 time 和 time2 欄位與當前時間戳一起快速估算連接的帶寬和/或延遲,但這不太可能有用。
下面的表格描述了握手過程的幾個階段
握手後,連接復用一個或多個消息塊流。每個塊流從一個消息流攜帶一種類型的消息。每個創建的塊都有一個與其關聯的唯一ID,稱為塊流ID。這些塊通過網路傳輸。發送時,每個塊必須在下一個塊之前全部發送。在接收端,根據塊流ID將塊組合成消息。
分塊允許將較高級別協議中的大的型消息分解為較小的消息,例如防止較大的低優先順序消息(例如視頻)阻塞較小的高優先順序消息(如音頻或控制)。
分塊還允許以較少的開銷發送小消息,因為分塊頭包含信息的壓縮表示信息,這些壓縮消息本來應該包含在消息本身的。
塊大小是可配置的。它可以使用 Set Chunk Size 控制消息進行設置。
每一個消息塊有頭部和數據組成,頭部自身可以被分割成三個部分:
消息塊基本頭(1到3個位元組) :這個欄位編碼了消息塊流的ID和消息塊的類型,消息塊類型決定了消息包頭的編碼格式,長度完全取決於可變長的消息塊流ID。
消息塊消息頭(0,3,7或11位元組) :這個欄位編碼正在傳送的消息的信息,長度可以利用在消息塊頭中詳細的消息塊類型來決定。
擴展時間戳(0或4位元組) :此欄位在某些情況下是存在的,取決於消息塊消息頭中的編碼時間戳或時間戳增量欄位。
消息塊塊數據(可變大小) :該塊的有效負載,直至配置的最大塊大小。
消息塊基本頭對消息塊流的ID和消息塊的類型進行編碼(在下面的圖表中用 fmt 表示),消息塊類型決定了編碼的消息頭的格式,消息塊基本頭欄位可以是1,2或者3個位元組長,取決於消息塊流ID。
該協議支持多達65597個ID為3-65599的流。 ID0,1和2被保留。 值0指示2位元組形式和64-319范圍內的ID( the second byte + 64 )。 值1表示3位元組形式,ID在64-65599( (the third byte) * 256 + the second byte + 64 )范圍內。 在3-63范圍內的值表示完整的流ID。 塊ID為2的流ID保留,用於低級別的協議控制消息和命令。
在消息塊基本頭中0-5比特(最不重要的)代表了消息塊流ID。
消息塊流ID 2-63 可以被編碼成這個欄位的單位元組的版本號。
塊流ID 64-319可以以2位元組的形式被編碼。 ID計算為(第二個位元組+ 64)。
可以在此欄位的3位元組版本中對塊流ID 64-65599進行編碼。 ID計算為((第三位元組)* 256 +(第二位元組)+64)。
cs id(6比特) :這個欄位包含了消息塊流ID,值從2到63,值0和1用於代表這個欄位的2個或者3個位元組的版本號。
fmt(2比特) :這個欄位標識消息塊消息頭使用的四種格式之一。見下一小節
cs id -64(8或者16個比特) :
這個欄位包含了消息塊流ID減64,例如ID 365在 cs id 段用1表示,在16比特的 cs id -64 段用301表示。
值為64到319的消息塊流ID可以被2位元組或者3位元組的版本號來表示。
在消息塊消息頭中有四種不同的格式,由消息塊基本頭的 fmt 欄位選擇。應該使用最簡潔的表達方式表示每一個消息塊消息頭。
類型0的消息塊有11個位元組長,這個類型必須在消息塊流開始時和消息流的時間戳回溯時使用
時間戳(3個位元組) :對於類型0的塊,消息的絕對時間戳發送到此處。 如果時間戳大於或等於16777215(十六進制 0xFFFFFF ),則該欄位必須是16777215,表示存在擴展時間戳欄位以編碼完整的32位時間戳。 否則,這個欄位應該是整個時間戳。
類型1的消息塊有7個位元組長,消息流ID沒有被包含,這個消息塊得到和先前消息塊同樣的流ID,帶有可變長的消息的流(例如許多視頻格式)在類型0消息塊後應該使用這種格式作為每一個消息的第一個消息塊。
類型2塊頭長度為3個位元組。 流ID和消息長度都不包含在內; 該塊與前面的塊具有相同的流ID和消息長度。 具有固定大小消息的流(例如,某些音頻和數據格式)應該在第一個消息之後使用這種格式作為每個消息的第一個塊。
類型3 的消息塊沒有頭,流ID,消息長度和時間戳delta,這個類型的消息塊在之前的消息塊中取值,當單一的消息被分裂成消息塊,所有的消息塊除了第一個,其餘都應該使用這種類型,流由同樣大小的消息組成。
塊消息頭中每個欄位的描述:
Extended Timestamp 欄位用於編碼大於16777215( 0xFFFFFF )的時間戳或時間戳增量; 也就是說,對於時間戳或時間戳增量,它們不適合類型0,1或2塊的24位欄位。 該欄位對完整的32位時間戳或時間戳增量進行編碼。 這個欄位用於表示將類型0塊的時間戳欄位或類型1或2塊的時間戳增量欄位設置為16777215( 0xFFFFFF )。 當相同塊流ID的最新類型0,1或2的塊指示存在擴展時間戳欄位時,該欄位出現在類型3的塊中。
共有2個示例
本例給出了一個簡單的音頻消息流,這個例子示範了信息的冗餘。
下表顯示了在此流中生成的塊。 從消息3開始,數據傳輸得到優化。 除此之外,每消息只有1位元組的開銷。
本例說明一個很長的消息被分割成很多消息塊。
這里是分割出來的消息塊
消息塊1的包頭數據詳細介紹了307個位元組的消息的全部。
注意這兩個例子,類型3消息塊可以用作兩種不同的方式,第一種是表示一條消息的延續,第二種是表示一條新消息的開始,這個新消息可以從已經存在的數據中衍生出來。
RTMP 塊流使用消息類型ID 1,2,3,5和6作為協議控制消息。 這些消息包含 RTMP Chunk Stream 協議所需的信息。
這些協議控制消息務必具有消息流ID 0 (稱為控制流)並且以塊流ID 2 發送。協議控制消息一旦被接收就會立即生效,同時時間戳被忽略。
協議控制消息1:設置消息塊大小。用來通知對方新的最大的消息塊大小。
消息塊的大小可以被設置成一個默認的值,128位元組,但是客戶端或者服務端可以改變這個值,並且發送消息通知對方更新。例如:假設一個客戶端想要發送131位元組的音頻數據,消息塊的大小為128位元組,在這種情況下,客戶端可以發送這個協議控制消息給服務端以通知消息塊的大小被設置成了131位元組,那麼客戶端就可以用一個消息塊發送音頻數據。
最大塊大小應該不能小於128個位元組,並且必須不能小於1個位元組。 每個方向的最大塊大小都是獨立維護的。
0 : 這一位必須為0。
chunk size 塊大小(31位) :該欄位保存新的最大塊大小(以位元組為單位),這將用於發件人的所有後續塊,直至另行通知。 有效大小為1到2147483647( 0x7FFFFFFF )(含); 但是,大於16777215( 0xFFFFFF )的所有大小都是等效的,因為沒有塊大於一條消息,並且沒有消息大於16777215位元組。
協議控制消息2:中止消息。用於通知對方是否正在等待塊完成消息,然後丟棄部分接收到的消息。 對方接收塊流ID作為該協議消息的有效載荷。 應用程序可能會在關閉時發送此消息,以指示不需要進一步處理消息。
chunk stream ID 塊流ID (32 位) : 該欄位保存塊流ID,對應的當前消息將被丟棄。
客戶端或伺服器在收到等於窗口大小的位元組後,必須向對端發送 Acknowledgement 確認。 窗口大小是發送方未收到接收方確認而發送的最大位元組數。 該消息指定了序列號,它是到當前為止收到的位元組數。
sequence number 序列號(32 位) :欄位表示到當前為止收到的位元組數。
客戶端或伺服器發送此消息以通知對方在發送 Acknowledgement 確認之間使用的窗口大小。 發送人希望在發送窗口大小位元組後得到對方的確認。
客戶端或伺服器發送此消息來限制另一方的輸出帶寬。 收到此消息的另一方通過將已發送但未確認的數據量限制為此消息中指示的窗口大小這種方式用來限制其輸出帶寬。如果窗口大小與發送給此消息發送者的最後一個窗口大小不同,那麼接收此消息的另一方應該使用 "Window Acknowledgement Size" 消息進行響應。
限制類型 Limit Type 是以下值之一:
本部分主要介紹 RTMP 消息的格式,在網路實體之間使用較低級傳輸層(如 RTMP塊流 )傳輸這些消息。
雖然 RTMP 旨在與 RTMP塊流 一起使用,但它可以使用任何其他傳輸協議發送消息。 RTMP Chunk Stream 和 RTMP 一起適用於各種音視頻應用,從一對一和一對多實時廣播到視頻點播服務,再到互動式會議應用。
伺服器和客戶端通過網路發送 RTMP 消息以相互通信。 消息可能包括音頻,視頻,數據或任何其他消息。
RTMP 消息有兩部分,頭部和有效負載。
消息頭包含以下欄位:
消息的另一部分是有效負載,它是消息中包含的實際數據。 例如,它可能是一些音頻樣本或壓縮的視頻數據。
RTMP使用消息類型ID 4 作為用戶控制消息。 這些消息包含RTMP流層使用的信息。 帶有ID 1,2,3,5和6的協議消息由RTMP塊流協議使用。
用戶控制消息應該使用消息流ID 0(稱為控制流),並且當通過RTMP塊流發送時,在消息流ID 2上發送。用戶控制消息在流中被接收時生效, 他們的時間戳被忽略。
客戶端或伺服器發送此消息以通知對端用戶控制事件。 該消息攜帶事件類型和事件數據。
消息數據 Event Data 的前2個位元組用於標識事件類型 Event Type 。 事件類型後面跟著事件數據。 事件數據欄位的大小是可變的。 但是,在消息必須通過RTMP塊流層的情況下,最大塊的大小應該足夠大,以允許這些消息適合單個塊。
本節介紹在伺服器和客戶端之間用於相互通信的不同類型的消息和命令。
在伺服器和客戶端之間交換的不同類型的消息包括用於發送音頻數據的音頻消息,用於發送視頻數據的視頻消息,用於發送任何用戶數據的數據消息,共享對象消息和命令消息。 共享對象消息提供了一種通用的方式來管理多個客戶端和伺服器之間的分布式數據。 命令消息在客戶端和伺服器之間傳送 AMF 編碼的命令。 客戶端或伺服器可以通過流使用命令消息請求對方的遠程過程調用( RPC )。
伺服器和客戶端通過網路發送消息以相互通信。 消息可以是任何類型,包括音頻消息,視頻消息,命令消息,共享對象消息,數據消息和用戶控制消息。
命令消息在客戶端和伺服器之間傳送 AMF 編碼命令。 這些消息的 AMF0 編碼的消息類型值為20, AMF3 編碼的消息類型值為17。 這些消息被發送來執行一些操作,例如 connect , createStream , publish , play , pause 等。 諸如 onstatus , result 等命令消息用於通知發送者有關請求的命令的狀態。 命令消息由命令名稱,事務ID和包含相關參數的命令對象組成。 客戶端或伺服器可以通過流使用命令消息請求對方的遠程過程調用( RPC )。
客戶端或伺服器發送此消息用於向對方發送元數據或任何用戶數據。 元數據包括有關數據(音頻,視頻等)的詳細信息,如創建時間,持續時間,主題等。 AMF0 的消息類型值為18, AMF3 的消息類型值為15。
共享對象是一個Flash對象(name-value對的集合),在多個客戶端,實例等之間同步的。 AMF0 的消息類型19和 AMF3 的消息類型16保留用於共享對象事件。 每條消息可以包含多個事件。
支持以下事件類型:
客戶端或伺服器發送此消息來向對等方發送音頻數據。 消息類型值8保留給音頻消息。
客戶端或伺服器發送此消息以向對等方發送視頻數據。 消息類型值9保留給視頻消息。
聚合消息是單個消息。消息類型22用於聚合消息。
聚合消息的消息流ID會覆蓋聚合內的子消息的消息流ID。
聚合消息的時間戳與第一個子消息之間的差異是用於將子消息的時間戳重新歸一化為流時間尺度的偏移量。 將偏移量添加到每個子消息的時間戳以達到標准化的流時間。 第一個子消息的時間戳應該與聚合消息的時間戳相同,所以偏移量應該為零。
後向指針包含前一個消息的大小,包括其頭部。 它被包含來匹配 FLV 文件的格式並用於向後搜索。
使用聚合消息有幾個性能優勢:
客戶端或伺服器發送此消息以通知對端關於用戶控制事件。
支持以下用戶控制事件類型:
客戶端和伺服器交換 AMF 編碼的命令。發送方發送一條命令消息,其中包含命令名稱,事務ID和包含相關參數的命令對象。例如, connect 命令包含 'app' 參數,它告訴客戶端連接到的伺服器應用程序名稱。接收方處理該命令並以相同的事務ID發送響應。響應字元串可以是 _result , _error 或方法名稱,例如 verifyClient 或 contactExternalServer 。
_result 或 _error 命令字元串表示響應。事務ID指示響應引用的未完成的命令。它與 IMAP 和許多其他協議中的標簽相同。命令字元串中的方法名稱指示發送方正試圖在接收方端運行方法。
以下類對象用於發送各種命令:
NetConnection 管理客戶端應用程序和伺服器之間的雙向連接。 另外,它為非同步遠程方法調用提供支持。
以下命令可以在 NetConnection 上發送:
客戶端向服務端發送連接( connect )命令請求連接一個伺服器應用實例。以下為命令的結構:
以下是連接命令的命令對象中使用的 name-value 對的描述:
audioCodecs 屬性的標志值:
videoCodecs 屬性的標志值:
videoFunction 屬性的標志值:
對象編碼( object Encoding )屬性的值:
以下是服務端到客戶端命令的結構:
以下是連接命令中的消息流:
命令執行期間的消息流是:
NetConnection 對象的調用方法在接收端運行遠程過程調用( RPC )。 被調用的 RPC 名稱作為參數傳遞給 call 命令。
從發送方到接收方的命令結構如下:
響應的命令結構如下:
客戶端將此命令發送到伺服器以創建用於消息通信的邏輯通道。音頻,視頻和元數據的發布是通過使用 createStream 命令創建的流通道執行的。
NetConnection 是默認通信通道,其流ID為0。協議和一些命令消息(包括 createStream )使用默認通信通道。
從客戶端到伺服器的命令結構如下所示:
從伺服器到客戶端的命令結構如下:
NetStream 定義了流式音頻,視頻和數據消息可以通過將客戶端連接到伺服器的 NetConnection 流動的通道。 一個 NetConnection 對象可以為多個數據流支持多個 NetStream 。
以下命令可以由客戶端在 NetStream 上發送到伺服器:
伺服器使用 'onStatus' 命令將 NetStream 狀態更新發送到客戶端:
客戶端將此命令發送到伺服器以播放流。 播放列表也可以使用此命令多次創建。
如果您想要創建一個可在不同直播流或錄像流之間切換的動態播放列表,請多次調用 play ,每次給 reset 傳遞 false 。相反,如果要立即播放指定的數據流,請清空播放隊列中的其他流,給 reset 傳遞 true 。
從客戶端到伺服器的命令結構如下所示:
Play 命令中的消息流:
㈡ RTMP協議抓包分析拉流過程
RTMP協議規定,播放一個流媒體有兩個前提步驟:
第一步,建立一個網路連接(NetConnection)。
第二步,建立一個網路流(NetStream)。
網路連接代表伺服器端應用程序和客戶端之間基礎的連通關系,網路流代表了發送多媒體數據的通道。伺服器和客戶端之間只能建立一個網路連接,但是基於該連接可以創建很多網路流。
播放一個RTMP協議的流媒體需要經過四個階段:
下面是使用librtmp執行拉流過程的API調用流,如下:
RTMP定義了較為完善的協議標准,但是每種播放工具的實現略有差異,下面是我使用VLC播放器拉流時抓取的報文,使用wireshark分析過程整理為下面的圖文。
先看一張總覽圖,圖中顯示的報文和時序包含了握手、建立連接、建立流和播放階段,如下:
還有申明下,以下的流程是根據實際抓包情況分析出來的,由於不同的工具省略了一些不必要的步驟,故不代表標准結果,僅供參考。
由於講解握手過程的文檔資料比較多,我這里就不重復描述了,摘圖如下:
個人認為這張圖是最符合標准時序的,細節拿捏得非常講究,雖然很多實現簡化了流程。
包括以下報文和步驟:
協議截圖如下:
協議方向:客戶端 -> 伺服器
塊頭欄位:
HeaderType: 0
CSID: 3
猛神 時間戳:0
BodySize: 201
枝坦虧 TypeID: 0x14
Stream ID: 0
負載格式:AMF0表示,connect 1 object1
object1屬性列表:
信睜 "app": "live"
"flashVer": "LNX 9,0,124,2"
"tcUrl": " rtmp://127.0.0.1:1935/live "
"fpad": false
"capabilities": 15,
"audioCodes": 4071,
"videoCodes": 252,
"videoFunction": 1,
End Of Object Marker
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 2
時間戳:0
BodySize: 4
TypeID: 0x05
Stream ID: 0
負載格式:4位元組整型表示,如5000000
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 2
時間戳:0
BodySize: 5
TypeID: 0x06
Stream ID: 0
負載格式:5位元組整型表示,前4位元組為帶寬,後1位元組為標志,如5000000, 2(動態調整)
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 2
時間戳:0
BodySize: 4
TypeID: 0x01
Stream ID: 0
負載格式:4位元組整型表示,如4096
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 3
時間戳:0
BodySize: 190
TypeID: 0x14
Stream ID: 0
負載格式:AMF0表示,_result 1 object1 object2
object1屬性列表:
"fmsVer": "FMS/3,0,1,123"
"capabilities": 31,
End Of Object Marker
object2屬性列表:
"level": "status"
"code": "NetConnection.Connect.Success",
"description": "Connection succeeded.",
"objectEncoding": 0
End Of Object Marker
協議截圖如下:
協議方向:客戶端 -> 伺服器
塊頭欄位:
HeaderType: 0
CSID: 2
時間戳:0
BodySize: 4
TypeID: 0x05
Stream ID: 0
負載格式:4位元組整型表示,如5000000
包括以下報文和步驟:
協議截圖如下:
協議方向:客戶端 -> 伺服器
塊頭欄位:
HeaderType: 1
CSID: 3
時間戳:0
BodySize: 25
TypeID: 0x14
負載格式:AMF0表示,createStream 2 object(Null)
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 3
時間戳:0
BodySize: 29
TypeID: 0x14
Stream ID: 0
負載格式:AMF0表示,_result 2 object(Null) Number(1)
包括以下報文和步驟:
協議截圖如下:
協議方向:客戶端 -> 伺服器
塊頭欄位:
HeaderType: 0
CSID: 8
時間戳:0
BodySize: 30
TypeID: 0x14
Stream ID: 1
負載格式:AMF0表示,play 4 Object(Null) String節目ID("a") Number開始時間(-2000)
協議截圖如下:
協議方向:客戶端 -> 伺服器
塊頭欄位:
HeaderType: 1
CSID: 2
時間戳:1
BodySize: 10
TypeID: 0x04
負載格式:Event Type,2位元組的類型(3) 4位元組的流ID(1) 4位元組的MS時間單位(3000)
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 2
時間戳:0
BodySize: 6
TypeID: 0x04
負載格式:Event Type,2位元組的類型(0) 4位元組的流ID(1)
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 5
時間戳:0
BodySize: 96
TypeID: 0x14
Stream ID: 1
負載格式:AMF0表示,onStatus 0 Object1(Null) object2
object2屬性列表:
"level": "status"
"code": "NetStream.Play.Start",
"description": "Start live",
End Of Object Marker
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 5
時間戳:0
BodySize: 387
TypeID: 0x12
Stream ID: 1
負載格式:AMF0表示,onMetaData object
object屬性列表:
"Server": "NGINX RTMP"
"width": 480,
"height": 270,
"displayWidth": 480,
"displayHeight": 270,
"ration": 0,
"framerate": 16,
"fps": 16,
"videodatarate": 193,
"videocodeid": 7,
"audiodatarate": 52,
"audiocodeid": 10,
"profile": "",
"level": "",
End Of Object Marker
協議截圖如下:
協議方向:伺服器 -> 客戶端
塊頭欄位:
HeaderType: 0
CSID: 6
時間戳:0
BodySize: 209
TypeID: 0x08
Stream ID: 1
負載格式:格式頭,媒體數據
結合以上分析,總結時序圖如下:
另外,關於HeaderType和CSID的運用,先歸納使用情況:
0x14(connect) HeaderType: 0 CSID: 3
0x05(Ack Window Size) HeaderType: 0 CSID: 2
0x06(BrandWidth) HeaderType: 0 CSID: 2
0x01(ChunkSize) HeaderType: 0 CSID: 2
0x14(connect _result) HeaderType: 0 CSID: 3
0x14(createStream) HeaderType: 1 CSID: 3
0x14(createStream _result) HeaderType: 0 CSID: 3
0x14(play) HeaderType: 0 CSID: 8
0x04(SetBufferMS) HeaderType: 1 CSID: 2
0x04(Stream Begin) HeaderType: 0 CSID: 2
0x14(play onStatus) HeaderType: 0 CSID: 5
0x12(onMetaData) HeaderType: 0 CSID: 5
0x08(audioData) HeaderType: 0 CSID: 6
0x09(videoData) HeaderType: 0 CSID: 7
總結:
關於HeaderType的運用,有以下規則:
createStream使用1號HeaderType,借用3號CSID之前的StreamID。
SetBufferMS使用1號HeaderType。
audioData和videoData視情況使用0、1、2、3號HeaderType。
關於CSID的運用,有以下規則: