做者:逸殊 審覈:泰一git
簡介
RTMP 在可靠流式傳輸(TCP)的基礎上提供了雙向的消息多路複用服務,在通信雙方之間傳輸與時間相關的並行流數據,如音頻,視頻和數據消息。協議實現方一般爲不一樣的消息類型指定不一樣的優先級,這樣在網絡帶寬受限時能改變底層傳輸順序。算法
定義
- 負載:包中所承載的數據。例如音頻或視頻數據。
- 包:一個數據包由固定頭部和所承載的數據組成。一些底層協議可能須要定義數據包的封裝格式。
- 端口:在一個計算機中用於區分不一樣目標的抽象定義。在 TCP/IP 協議中用一個小的正整數來表示端口。OSI 傳輸層的傳輸選擇器就至關於端口。
- 傳輸地址:標識一個傳輸終端的網絡地址和端口的組合,例如 IP 地址和 TCP 端口的組合。
- 消息流:容許消息傳播的邏輯通道。
- 消息流 ID:每一個消息都會有一個對應的 ID,用於標識其所在的消息流。
- 塊:消息的一個片斷。消息在傳輸以前會被分割成更小的片斷,由於每一塊都很小,以致於能夠給不一樣的塊指定各自的優先級,經過這種方式保證多個流中數據能夠按照時間戳的順序傳輸。
- 塊流:塊向某一肯定方向傳播的邏輯通道。能夠是客戶端到服務端,也能夠是服務端到客戶端。
- 塊流 ID:每一個塊都會有一個對應的 ID,用於標識其所在的塊流。
- 複用:將獨立的音頻 / 視頻數據整合爲統一的音視頻流,可使多個音視頻流同步傳輸。
- 複用分離:複用的逆向過程。將合併的音視頻數據分離爲原始的音頻和視頻數據。
- 遠程過程調用:客戶端或服務端調用另外一端的功能。
- 元數據:媒體數據的描述信息。
- 應用實例:服務器上能夠和 Client 創建鏈接的應用。
- 動做消息格式:一個可用於序列化 ActionScript 對象圖的緊湊的二進制格式。
- 字節序:字節的順序,即多字節類型的數據在內存中的存放順序。TCP/IP 各層協議將字節序定義爲大端字節序,所以 TCP/IP 協議中使用的字節序一般稱之爲網絡字節序。
- 大字節序:高位字節排放在內存的低地址,低位字節排放在內存的高地址。
- 小字節序:低位字節排放在內存的低地址,高位字節排放在內存的高地址。
字節序,校準,時間格式
全部整數都是以網絡字節序來表示的。除非另行說明,本文中的全部數字都是十進制數。 在沒有特殊說明的狀況下,RTMP 中的數據都是字節對齊的。若是有填充的話,填充字節應該用 0。 RTMP 中的時間戳是用一個整數來表示的,表明相對於一個起始時間的毫秒數。一般每一個流的時間戳都從 0 開始,但這不是必須的,只要通信雙方使用統一的起始時間就能夠了。要注意的是,跨流的時間同步(不一樣主機之間)須要額外的機制來實現。 因爲時間戳的長度只有 32 位,因此只能在 50 天內循環(49 天 17 小時 2 分鐘 47.296 秒)。而流是能夠不斷運行的,可能多年纔會結束。因此 RTMP 應用在處理時間戳是應該使用連續的數字算法,而且應該支持迴環處理。例如:一個應用能夠假設全部相鄰的時間戳間隔不超過 2^31-1 毫秒,在此基礎上,10000 在 4000000000 以後,3000000000 在 4000000000 以前。 時間戳增量也是以毫秒爲單位的無符號整數。時間戳增量可能會是 24 位長度也多是 32 位長度。緩存
RTMP 塊流
塊流爲上層流媒體協議提供複用和分包的功能。RTMP 塊流是爲配合 RTMP 協議而設計,但它可使用在任何發送消息流的協議中。每一個消息包含時間戳和負載類型信息。RTMP 塊流和 RTMP 協議組合能夠適用於多種音視頻應用,從一對一或一對多直播到視頻會議都能很好的知足。 當使用可靠傳輸協議(如 TCP)時,RTMP 塊流爲全部消息提供了可靠的跨流端對端按時間戳順序發送的機制。RTMP 塊流不提供優先級控制,可是能夠由上層協議提供這樣的優先級。例如:當某個客戶端網絡比較慢時,可能會選擇拋棄一些視頻消息來保證聲音消息可以及時接收。 RTMP 塊流除自身內置的協議控制消息外,還爲上層協議提供了用戶控制消息的機制。安全
消息格式
消息格式由上層協議定義,消息能夠被分紅多個塊以支持多路複用。消息應該包含分塊功能所需的全部字段,具體內容以下:服務器
- 時間戳(4-byte):消息的時間戳。
- 長度(3-byte):消息有效負載的長度,若是消息頭不能被省略,則消息頭的長度也應該包含在長度中。
- 類型 ID(1-byte):消息類型 ID。一些類型 ID 是爲協議控制消息保留的,這些消息所表示的信息同時供 RTMP 塊流協議和上層協議使用。全部其餘類型 ID 都用於上層協議,RTMP 塊流對這些 ID 作不透明處理。實際上,RTMP 塊流不須要用這些值來區分類型,全部消息均可以是相同的類型,應用也能夠用本字段來區分同步軌道而不是區分類型。
- 消息流 ID(4-byte):消息流 ID 能夠是任意值。被複合到同一個塊流的消息流,依據消息流 ID 進行分離。另外,就相關的塊流而言,這個值是不透明的。這個字段使用小字節序。
握手
RTMP 鏈接以握手開始,它的握手過程可能和其餘協議不一樣,這裏的握手由 3 個固定大小的塊組成,而不是可變大小的塊加上固定大小的頭。網絡
握手流程
握手由客戶端發送 C0 和 C1 塊開始。 客戶端必須等接收到 S1 以後才能夠發送 C2。客戶端必須等接收到 S2 以後才能夠發送其餘數據。 服務器必須等接收到 C0 以後才能夠發送 S0 和 S1,也可能接收到 C1 以後發送。服務器必須等接收到 C1 以後才能夠發送 S2。服務器必須等接收到 C2 以後才能夠發送其餘數據。app
C0 和 S0 格式
C0 和 S0 是單獨的一個字節,能夠當作一個 8bit 的整數字段來對待。 異步
如下是 C0 和 S0 包的字段解釋:ide
- 版本號(8 位): 在 C0 包中,該字段表示客戶端請求的 RTMP 版本。在 S0 中,該字段表示服務器選擇的 RTMP 版本。本規範所定義的版本是 3。可選值中,0-2 是早期版本所用的,已被丟棄,4-31 保留在將來使用,32-255 不容許使用(爲了區分其餘以某一可見字符開始的文本協議)。若是服務器不能識別客戶端請求的版本,應該返回 3,客戶端可能選擇降級到版本 3,也可能放棄握手。
C1 和 S1 格式
C1 和 S1 包固定爲 1536 字節,包含如下字段: 性能
- 時間戳(4 字節):該字段承載一個時間戳,該時間戳應該做爲發送端點全部後續塊的時間戳起始時間。能夠是 0,也能夠是其餘的任意值。爲了同步多個塊流,端點可能會發送其餘塊流的當前時間戳。
- 零值(4 字節):該字段全部值都必須爲 0。
- 隨機數據(1528 字節):該字段能夠是任意值。經過這個字段來區分本身和鏈接的另外一方,因此此數據應該有充分的隨機性,可是不必使用加密安全的隨機值或動態值。
C2 和 S2 格式
C2 和 S2 包的長度也爲 1536 字節,基本上是 S1 和 C1 的回傳,包含如下字段:
- 時間戳(4 字節):該字段必須包含對端發來的時間戳(對 C2 來講是 S1, 對 S2 來講是 C1)。
- 時間 2(4 字節):該字段必須包含先前發送的並被對端讀取的包的時間戳。(對 C2 來講是 C1,對 S2 來講是 S1)。
- 隨機數據回顯(1528 字節):該字段必須包含對端發送過來的隨機數據字段值(對 C2 來講是 S1, 對 S2 來講是 C1)。任何一端均可以用時間戳和時間戳 2 兩個字段值和當前時間戳來快速的估算帶寬和延遲,但這樣可能並不實用。
握手流程示意圖
上圖提到的狀態的解釋以下:
- Uninitialized:未初始化狀態。在該階段發送協議版本。客戶端在 C0 包中發送 RTMP 協議版本,若是服務器支持此版本,服務器將在響應中發送 S0 和 S1。若是不支持,服務器採用適當的行爲做爲響應,在 RTMP 規範中是終止鏈接。
- Version Send:版本已發送狀態。在未初始化狀態以後客戶端和服務端都進入版本已發送狀態。客戶端等待接收 S1 包,服務端等待接收 C1 包。收到所等待的包後,客戶端發送 C2 包,服務端發送 S2 包。以後狀態進入發送確認狀態。
- Ack Send:客戶端和服務端等待接收 S2 和 C2 包,收到後進入握手完成狀態。
- Handshake Done:握手完成, 客戶端和服務端開始交換消息。
分塊
握手完成後,一個或多個塊流可能會複用同一個鏈接,每一個塊流承載來自同一個消息流的同一類消息。每一個塊都有一個惟一的塊流 ID,這些塊經過網絡進行傳輸。在傳輸過程當中,必須一個塊發送完畢以後再發送下一個塊。在接收端,將全部塊根據塊中的塊流 ID 組裝成消息。 分塊將上層協議的大消息分割成小的消息,保證大的低優先級消息(好比視頻)不阻塞小的高優先級消息(好比音頻或控制消息)。 分塊還能下降消息發送的開銷,它在塊頭中包含了壓縮的本來須要在消息中所包含的信息。 塊大小是可配置的,這個能夠經過一個設置塊大小控制消息進行設定修改。越大的塊 CPU 使用率越低,可是在低帶寬的狀況下,大的寫入會阻塞其餘內容的寫入。而小一些的塊不適合高比特率的流。
塊格式
每一個塊由塊頭和數據組成,塊頭包含 3 部分:基本頭、消息頭和擴展時間戳。
- 基本頭 (1-3 字節):塊流 ID 和塊類型,塊類型決定了以後消息頭的編碼格式。基本頭的長度取決於塊流 ID,當塊流 ID 越大時所須要的字節數越多。
- 消息頭 (0,3,7 或 11 字節):所發送消息的描述信息。該部分的長度取決於基本頭中指定的塊類型。
- 擴展時間戳 (0 或 4 字節):該部分只有在某些特殊狀況下才會使用,是否使用取決於時間戳或時間戳增量是否超出了塊消息頭中相應字段的描述範圍。
- 塊數據 (變長):塊承載的有效數據,最大長度爲配置的塊大小。
基本頭
基本頭包含塊流 ID 和塊類型(在下圖中用 fmt 字段表示),塊類型決定了消息頭的編碼格式,基本頭長度多是 1,2 或 3 字節,這取決於塊流 ID 的長度。 協議實現方應該用可以用最短表示法來表示塊流 ID。 RTMP 最多支持 65597 個流,ID 在 3-65599 範圍內,0,1,2 爲保留值。若是 2~7 位表明的值爲 0 表示塊基本頭佔 2 個字節,而且塊流 ID 範圍在 64-319 之間(第二個字節 + 64),若是 2~7 位表明的值爲 1 表示塊基本頭佔 3 個字節,而且 ID 範圍在 64-65599 之間(第三個字節 * 256 + 第二個字節 + 64),當 ID 在 3-63 之間時直接使用 2~7 位的值來表示流 ID。 2-63 範圍內的塊流 ID 用 1 個字節來編碼:
64-319 範圍內的塊流 ID 用 2 個字節來編碼,塊流 ID 爲計算所得,公式爲:第二個字節值 + 64:
64-65599 範圍內的塊流 ID 用 3 個字節來編碼,塊流 ID 爲計算所得,公式爲:第三個字節值 * 255 + 第二個字節值 + 64
上述圖中各個部分的含義以下:
- cs id (6 位):該字段表示完整的塊流 ID,取值在 2-63 之間。0,1 兩個值是保留值,用來表示基本頭是 2 字節仍是 3 字節長度。
- fmt:該字段代表了消息頭使用的格式。
- cs id - 64 (8 位或 16 位):該字段表示塊流 ID,取值在 64-63399 之間。
64-319 範圍內的塊流 ID 能夠用 2 字節來表示,也能夠用 3 字節表示。
消息頭
消息頭共有 4 種不一樣的格式,根據基本頭中的 "fmt" 字段值來肯定。協議實現方應該用最緊湊的格式來表示塊消息頭。
類型 0
0 類型的塊消息頭佔 11 個字節長度,該類型必須用在一個塊流的開頭,和每當塊流時間戳回退的時候(例如視頻回退的操做)。
- timestamp (3 字節):對於 0 類型的消息塊,消息的絕對時間戳在這裏發送。 若是時間戳大於或等於 16777215 (0xFFFFFF),改字段值必須爲 16777215,而且必須設置擴展時間戳來共同編碼 32 位的時間戳。不然該字段就是完整的時間戳。
- message length (3 字節): 消息長度,類型 0 和類型 1 的塊包含此字段,表示消息的長度。要注意的是,一般消息長度與塊長度並不相同。塊長度除了最後一個塊以外,都與塊最大長度相同。
- message type id (3 字節): 消息類型 id,類型 0 和類型 1 的塊包含此字段,表示消息的類型。
- message stream id (4 字節): 消息流 ID,類型 0 的塊包含此字段,表示消息流 ID。消息流 ID 以小字節序存儲。一般,相同塊流中的消息屬於用一個消息流。雖然,不一樣的消息流複用相同的塊流會致使消息頭沒法有效壓縮,可是當一個消息流已關閉,準備打開另一個消息流時,就能夠經過發送一個新的 0 類型塊來實現複用。
類型 1
1 類型的塊消息頭佔用 7 個字節長度,不包含消息流 ID,該塊沿用上一個消息的消息流 ID。對於傳輸大小可變消息的流(如多數視頻格式),在發送第一個消息以後的每一個消息都應該使用該類型格式。
- timestamp delta (3 字節): 時間戳增量。類型 1 和類型 2 的塊包含此字段,表示前一個塊的 timestamp 字段和當前塊 timestamp 間的差值。 若是時間戳增量大於或等於 16777215 (0xFFFFFF),該字段必須爲 16777215,而且必須設置擴展時間戳,來共同表示 32 位的時間戳增量,不然該字段值就是實際的時間戳增量。
類型 2
2 類型的塊消息頭佔用 3 個字節長度,不包含消息流 ID 和消息長度,沿用上一個塊的消息流 ID 和消息長度。對於傳輸固定大小消息的流(如音頻和數據格式),在發送第一個消息以後的每個消息都應該使用該類型格式。
類型 3
3 類型的塊沒有消息頭,消息流 ID、消息長度和時間戳增量,該類型的塊使用和上一個塊相同的頭數據。當一個消息被分割成塊時,除了第一個塊,其餘塊都應該使用該類型。由相同大小、消息流 ID 和時間間隔的消息組成的流,在類型 2 的塊以後全部塊都應該使用該類型格式。若是第一個消息和第二消息之間的時間增量與第一個消息的時間戳相同,則 0 類型的塊以後能夠立刻發送 3 類型的塊,而沒必要使用 2 類型的塊來註冊時間增量。若是類型 3 的塊跟在類型 0 的塊後面,那麼 3 類型塊的時間戳增量與 0 類型塊的時間戳相同。
擴展時間戳
擴展時間戳用來輔助編碼超過 16777215 (0xFFFFFF) 的時間戳或時間戳增量,也就說消息頭沒法用 24 位數字來表示時間戳或時間戳增量時,既 0 類型塊的時間戳字段或 1,2 類型的時間戳增量字段值爲 16777215 (0xFFFFFF)。當最近的屬於相同塊流 ID 的 0 類型塊、1 類型塊或 2 類型塊有此字段時有此字段時,3 類型塊也應該有此字段。
示例
示例 1
這是一個簡單的音頻流消息,這是示例示範了信息冗餘。
下圖展現該消息流以塊流形式發送。從 3 類型塊開始了數據傳輸優化,以後的塊只附加了一個字節。
示例 2
該示例展現了一個超過 128 字節長度的消息,消息被分割成了數個塊。
下圖是被分割成的塊:
第一個塊的頭信息指明瞭消息總大小爲 307 字節。 注意這兩個示例,3 類型塊能夠在兩種狀況下使用。第一種狀況是消息拆分紅多個塊,另外一種狀況是新消息複用上一個消息的全部頭部內容。
協議控制消息
RTMP 塊流用消息類型 1,2,3,5 和 6 來做爲協議控制消息,這些消息包含 RTMP 塊流協議所須要的信息。 這些協議控制消息必須用 0 做爲消息流 ID (控制流 ID),並在 ID 爲 2 的塊流中發送。協議控制消息收到後當即生效,它們的時間戳信息是被忽略的。
設置塊大小
協議控制消息類型 1:設置塊大小,用於通知另外一端新的最大塊大小。 最大塊大小默認爲 128 字節,客戶端或服務端能夠修改此值,並用該消息通知另外一端。例如,假設一個客戶端想要發送 131 字節的音頻數據,而最大塊大小爲 128。在這種狀況下,客戶端能夠向服務端發送該消息,通知它最大塊大小被設置爲了 131 字節。這樣客戶端只用一個塊就能夠發送這些音頻數據。 最大塊大小不能小於 1 字節,一般應該不低於 128 字節。每一個方向上的最大塊大小是獨立的。
- 0 (1 位): 該位必須爲 0.
- chunk size (31 位): 該字段以字節形式保存新的最大塊大小,該值將用於後續的全部塊的發送,直到收到新的通知。該值可取值範圍爲 1-2147483647 (0x7FFFFFFF),可是全部大於 1677215 (0xFFFFFF) 的值都是視做是 16777215,由於任何塊不可能比消息大,而消息長度不能大於 16777215 字節。
終止消息
協議控制消息類型 2:終止消息,通知正在等待消息後續塊的另外一端,能夠丟棄指定塊流接收到的數據,塊流 ID 爲該消息的載荷。應用可能在關閉的時候發送該消息,用來代表後面的消息沒有必要繼續處理了。
- chunk stream id (32 字節): 指定消息的塊流 ID。
確認消息
客戶端或服務器在收到數據總長和窗口大小相等時,經過它回覆確認消息。在鏈接創建完成後,消息的發送方會通知接收方一個窗口的大小(指定一個長度),若是接收方收到指定長度的數據後沒有發送回覆消息,發送方就不會再發送任何內容了。
- sequence number (32 字節): 到當前時刻爲止接收到的字節總數。
確認窗口大小
客戶端或服務端發送該消息來通知對端發送確認消息所使用的視窗大小,並等待接收端發送確認消息。接收端在接收到視窗大小後必須發送確認消息。
設置對方傳輸帶寬
客戶端或服務端發送該消息來限制對端的輸出帶寬。接收端收到消息後,能夠直接使用消息中指定的窗口大小,而不須要等待收到確認消息以後。若是視窗大小與上一個視窗大小不一樣,則該消息的接收端應該向該消息的發送端發送新的窗口大小消息。這個消息和上一個消息都是調整窗口大小的,不一樣的地方是,這個消息是接收者請求發送者,讓它調整窗口大小,而上一個消息是發送者主動設置了窗口大小,通知數據接收者。
Limit Type(限制類型)有如下值:
- 0 - Hard: 應該將輸出帶寬限制爲指定視窗大小。
- 1 - Soft: 應該將輸出帶寬限制爲指定視窗大小和當前視窗大小中較小的值。
- 2 - Dynamic: 若是上一個消息的限制類型爲 Hard,則該消息一樣爲 Hard,不然拋棄該消息。
RTMP 消息格式
雖然 RTMP 被設計成使用 RTMP 塊流傳輸,可是它也可使用其餘傳輸協議來發送消息,在這種狀況下 RTMP 消息的格式以下所示。值得一提的是,RTMP 塊流協議和 RTMP 協議配合時,很是適合音視頻應用,包括單播、一對多實時直播、視頻點播和視頻會議等。
格式
服務端和客戶端經過在網絡上發送 RTMP 消息實現之間的交互,消息包括音頻、視頻、數據等。 RTMP 消息包含兩部分,消息頭和有效負載。
RTMP 消息頭
消息頭包含如下信息:
- Message Type: 消息類型,佔用 1 個字節。1-6 的消息類型 ID 是爲協議控制消息保留的。
- Length: 有效負載的字節數,佔用 3 個字節。該字段是用大端序表示的。
- Timestamp: 時間戳,佔用 4 個字節,用大端序表示。
- Message Stream Id: 消息流 ID,標識消息所使用的流,用大端序表示。
消息有效負載
消息的另外一部分就是有效負載,也是消息包含的實際數據,能夠是音頻樣本或者壓縮的視頻數據。
用戶控制消息
RTMP 協議將消息類型 4 做爲用戶控制消息 ID,這些消息包含 RTMP 流所需的必要信息。消息類型 1,2,3,5 和 6 由 RTMP 塊流協議使用。 用戶控制消息應該使用 ID 爲 0 的消息流(控制流),而且經過 RTMP 塊流傳輸時使用 ID 爲 2 的塊流。用戶控制消息收到後當即生效,它們的時間戳信息會被忽略。 客戶端或服務端經過發送該消息告知對方用戶控制事件。該消息攜帶事件類型和事件數據兩部分。
開頭的 2 個字節用於指定事件類型,緊跟着是事件數據。事件數據字段長度可變,可是若是用 RTMP 塊流傳輸,則消息總長度不能超過最大塊大小,以使消息可使用一個單獨的塊進行傳輸。
RTMP 指令消息
各類類型的消息在客戶端和服務端之間進行交換,包括用於發送音頻數據的音頻消息,用於發送視頻數據的視頻消息,用於發送任意用戶數據的數據消息,共享對象消息和指令消息等。共享對象消息的主要用途是管理客戶端和相同服務器的共享數據。指令消息發送的是客戶端與服務端之間的 AMF 編碼指令,客戶端或服務端也能夠經過指令消息來實現遠程過程調用(RPC)。
消息類型
客戶端和服務端經過在網絡上發送消息來實現交互,消息能夠是任意類型,包括音頻消息、視頻消息、指令消息、共享對象消息、數據消息和用戶控制消息等。
指令消息
指令消息在客戶端和服務端之間傳遞 AMF 編碼的指令,消息類型 20 表明 AMF0 編碼,消息類型 17 表明 AMF3 編碼。發送這些消息來完成鏈接、建立流、發佈、播放、暫停等操做。像狀態、結果這樣的指令消息,用於通知發送方請求的指令狀態。一條指令消息由指令名、事務 ID 和包含相關參數的指令對象組成。客戶端或服務端還能夠經過指令消息來實現遠程過程調用 (RPC)。
數據消息
客戶端或服務端經過該消息來發送元數據或其餘用戶數據。元數據包括數據 (音頻、視頻) 的建立時間、時長、主題等詳細信息。消息類型 18 表明 AMF0 編碼,消息類型 15 表明 AMF3 編碼。
共享對象消息
共享對象是在多個客戶端之間同步的 Flash 對象 (鍵值對集合)。消息類型 19 表明 AMF0 編碼,消息類型 16 表明 AMF3 編碼。每一個消息均可以包含多個事件。
支持如下事件類型:
- 建立(1):客戶端向服務端發送,請求建立指定名稱的共享對象。
- 釋放(2):客戶端通知服務端,共享對象已在本地刪除。
- 請求更新(3):客戶端請求修改共享對象的屬性值。
- 更新(4):通知服務端向除本身外的其餘客戶端發送共享數據消息,通知它們有屬性的值發生了變化。
- 成功(5):「請求更新」 事件被接受後,服務端向發送請求的客戶端回覆此事件。
- 發送消息(6):客戶端向服務端發送此事件,來廣播一個消息。服務端收到此事件後向全部客戶端廣播一條消息,包括請求方客戶端。
- 狀態(7):服務端發送此事件來通知客戶端錯誤信息。
- 清除(8):服務端向客戶端發送此事件,通知客戶端清除一個共享對象。服務端在回覆客戶端的 「建立」 事件時也會發送此事件。
- 移除(9):服務端發送此事件,使客戶端刪除一個插槽。
- 請求移除(10):客戶端刪除一個插槽時發送此事件。
- 建立成功(11):當鏈接成功時服務端向客戶端發送此事件。
音頻消息
客戶端或服務端經過發送此消息來發送音頻數據給對方,消息類型 8 是爲音頻消息預留的。
視頻消息
客戶端或服務端經過發送此消息來發送視頻數據給對方,消息類型 9 是爲視頻消息預留的。
組合消息
組合消息,是一個消息包含多個子 RTMP 消息,子消息符合 RTMP 消息格式。消息類型 22 用於組合消息。
組合消息的消息流 ID 會覆蓋其中子消息的消息流 ID。 組合消息的時間戳和其中第一個子消息的時間戳的差值,是用來將全部子消息的時間戳重整爲流時間的位移量。位移量會加到每個子消息的時間戳上來換算出正常的流時間。第一個子消息的時間戳應該與組合消息的時間戳相同,因此位移量應該爲 0。 Back Pointer (反向指針) 包含前一個消息的長度(包括消息頭),這樣符合 flv 文件格式,可用於進行後退操做。 使用組合消息有如下好處:
- 塊流協議中,一個塊最多隻能發送一個消息,這樣就使用組合消息,加大塊大小,從而下降發送的塊數量。
- 子消息在內存中連續存放,這樣系統調用網絡發送數據的性能更高。
用戶控制消息事件
客戶端或服務器經過該消息發送用戶控制事件。
用戶控制消息支持如下事件:
- 流開始(0):服務端發送該事件,用來通知客戶端一個流已經能夠用來通信了。默認狀況下,該事件是在收到客戶端鏈接指令併成功處理後發送的第一個事件。事件的數據使用 4 個字節來表示可用的流的 ID。
- 流結束(1):服務端發送該事件,用來通知客戶端其在流中請求的數據已經結束了。若是沒有額外的指令,將不會再發送任何數據,而客戶端會丟棄以後從該流接收到的消息。事件數據使用 4 個字節來表示回放完成的流的 ID。
- 流枯竭(2):服務端發送該事件,用來通知客戶端流中已經沒有更多的數據了。若是服務端在必定時間後沒有探測到更多數據,它就能夠通知全部訂閱該流的客戶端,流已經枯竭。事件數據用 4 個字節來表示枯竭的流的 ID。
- 設置緩衝區大小(3):客戶端發送該事件,用來告知服務端用來緩存流中數據的緩衝區大小 (單位毫秒)。該事件在服務端開始處理流數據以前發送。事件數據中,前 4 個字節用來表示流 ID,以後的 4 個字節用來表示緩衝區大小(單位毫秒)。
- 流已錄製(4):服務端發送該事件,用來通知客戶端指定流是一個錄製流。事件數據用 4 個字節表示錄製流的 ID。
- ping 請求(5):服務端發送該事件,用來探測客戶端是否處於可達狀態。事件數據是一個 4 字節的時間戳,表示服務端分發該事件時的服務器本地時間。客戶端收到後用 ping 響應回覆服務端。
- ping 響應(6):客戶端用該事件回覆服務端的 ping 請求,事件數據爲收到的 ping 請求中攜帶的 4 字節的時間戳。
指令類型
客戶端和服務器交換 AMF 編碼的指令。發送端發送一條指令消息,其中包含了指令名稱、處理 ID、以及含有相關參數的指令對象。例如,鏈接指令消息包含了’app' 參數,以告知服務器客戶端但願鏈接的目標程序。接收端處理這條指令並回復含有一樣處理 ID 的響應。回覆的字符串可能爲_result、_error 或方法名。如 verifyClient 或 contactExternalServer. _result 或_error 的指令字符表明一條響應,處理 ID 則代表回覆是針對哪條指令的,這在 IMAP 或其餘協議中是徹底相同的。指令字符串中的方法名代表發送端但願運行接收端上的一個方法。 指令消息可分爲以下兩類:
- NetConnection:一個服務器和客戶端之間鏈接的高層表現對象。
- NetStream:一個音頻流、視頻流及其餘相關數據傳輸流,咱們會發送如播放、暫停等指令來控制數據流動。
NetConnection 指令
NetConnection 管理着一個客戶端程序和服務器之間的雙向鏈接,除此以外,它還提供了對異步遠程方法調用的支持。 下列指令可經過 NetConnection 進行發送:
- Connect
- Call
- Close
- CreateStream
Connect
客戶端發送 connect 指令至服務器端以請求鏈接至某一服務器程序實例。 指令結構以下:
Connect 指令中會用到的鍵值對:
音頻編碼:
視頻編碼:
視頻功能:
對象編碼:
由服務器發送至客戶端的指令結構以下:
指令執行流程:
指令執行的消息流以下:
- 客戶端發送 connect 指令至服務器以請求鏈接至服務器端程序實例。
- 在收到鏈接指令後,服務器端發送協議消息 'Window Acknowledgement Size' 給客戶端。同時,服務器端還會鏈接 connect 指令中提到的應用。
- 服務器端發送協議消息‘Set Peer Bandwidth’至客戶端。
- 客戶端成功處理‘Set Peer Bandwidth’後發送協議消息‘Window Acknowledgement Size' 給服務器端。
- 服務器端發送用戶控制消息(StreamBegin)協議消息給客戶端。
- 服務器端發送指令消息以通知客戶端鏈接狀態(success/fail)。該指令中含有處理 ID (與 1 中收到相同),該消息同時還制定了部分屬性,如 Flash Media Server 版本(string)。除此以外,它還指定了鏈接響應相關的信息如 level (string),code (string),description (string),object-encoding (number) 等。
Call
NetConnection 對象的 call 方法用於遠程調用接收端上的程序。須要遠程調用的程序名稱經過一個參數傳遞給 call 指令。 發送指令結構以下:
響應指令結構以下:
CreateStream
客戶端發送該指令至服務器端以建立一條用於傳遞消息的邏輯通道,從而能夠利用已建立的流通道發佈音頻、視頻和元數據。 NetConnection 是默認的通信通道,流 ID 爲 0。協議和一些指令消息,包括 createStream,使用默認通信通道。 客戶端發出的指令結構以下:
服務器發出的指令結構以下:
NetStream 指令
基於 NetConnection 的客戶端至服務器間鏈接,NetStream 定義了一條能夠傳遞音頻流、視頻流以及消息流的通道。NetConnection 對象支持多個 NetStreams 以傳輸多個數據流。 客戶端可在 NetStream 中發送下列指令至服務器:
- Play
- Play2
- DeleteStream
- CloseStream
- ReceiveAudio
- ReceiveVideo
- Publish
- Seek
- Pause
服務器端經過 「onStatus" 將 NetStream 的狀態更新至客戶端:
Play
客戶端發送該指令值以播放一個流。屢次調用該指令也可建立一個播放清單。 若是你但願建立一個在不一樣 live 或 recorded 流間切換的動態播放清單,須要屢次調用 play 並傳遞 false 以免每次 reset。相反地,若是你但願當即播放某一指定流,傳遞 true 以清除等待播放隊列中的全部其餘流。 客戶端發送的指令結構以下:
流程圖以下:
指令執行期間的消息流以下:
- 客戶端在接收到來自服務器的 createStream 指令的成功結果後發送 play 指令。
- 在接收到 play 指令後,服務器發送協議數據來設置塊大小。
- 服務器發送一些另一個協議數據 (用戶控制),在這個消息裏包含事件 「StreamIsRecord」 和流 ID。這個消息的前 2 個字節是事件類型隨後的 4 字節是流 ID。
- 服務器向客戶端發送另一個協議消息 (用戶控制),這個消息指示了 「StreamBegin」 事件,表示流開始了。
- 若是客戶端向服務器發送的 play 指令成功執行了,服務器會發送 onStatus 指令消息包含 NetStream.Play.Start 或 NetStream.Play.Reset。僅當客戶端發送的 play 指令中的設置了 reset 標誌 NetStream.Play.Reset 纔會被髮送。若是播放的流不存在,服務器會在發送 onStatus 消息中包含 NetStream.Play.StreamNotFound。隨後,服務器就發送客戶端播放的音頻和視頻數據。
Play2
不一樣於 play 指令,play2 能夠切換碼率而不改變播放內容的時間軸。服務器爲客戶端能夠在 play2 中請求的全部支持的碼率維護多個字段。 客戶端發送的指令結構以下:
該指令的消息流程以下圖:
DeleteStream
當 NetStream 對象將要被銷燬時,它發送該 deleteStream 指令。 客戶端發送的指令結構以下:
服務器不須要發送任何應答。
ReceiveAudio
NetStream 發送 ReceiveAudio 消息通知服務器是否發送或不發送音頻到客戶端。 客戶端發送的指令結構以下:
若是 receiveAudio 指令發送帶有 flase 的 bool flag,服務器不發送任何響應。若是這個標誌被設置爲 true,服務器應答 NetStream.Seek.Notify 和 NetStream.Play.Start 的狀態消息。
ReceiveVideo
NetStream 發送 ReceiveVideo 消息通知服務器是否發送或不發送視頻到客戶端。 客戶端發送的指令結構以下:
若是 receiveVideo 指令發送帶有 flase 的 bool flag,服務器不發送任何響應。若是這個標誌被設置爲 true,服務器應答 NetStream.Seek.Notify 和 NetStream.Play.Start 的狀態消息。
Publish
客戶端發送 publish 指令將已命名的流發佈到服務器上。使用這個名稱,任何客戶端均可以播放此流,並接收已發佈的音頻、視頻和數據消息。 客戶端發送的指令結構以下:
服務器應答 onStatus 指令,以標記發佈的開始。
Seek
客戶端發送 seek 指令以定位媒體文件內或者播放列表的某個位置(以毫秒爲單位)。 客戶端發送的指令結構以下:
當定位成功,服務器發送 NetStream.Seek.Notify 的狀態消息。失敗的時候,它返回一個_error 的消息。
Pause
客戶端發送 pause 指令以告訴服務器暫停或者開始播放。 客戶端發送的指令結構以下:
當流被暫停,服務器發送一個 NetStream.Pause.Notify 的狀態消息。當一個流變成未暫停狀態,NetStream.Unpause.Notify 被髮送。失敗的時候,它返回一個_error 的消息。
消息交換例子
這裏是一些樣例,以解釋使用 RTMP 的消息交換。
發佈視頻
這個例子說明了一個發佈者如何發佈一個流並將視頻流推到服務器上。其餘客戶端能夠訂閱這個已發佈的流,並播放視頻。
廣播一個共享對象消息
這個例子說明了在建立和更改共享對象時所交換的消息。它也說明了共享對象消息廣播的過程。
發佈媒體流元數據
這個例子描述了發佈元數據的消息交換。
參考內容
[1] RTMP 規範 [2] RTMP 協議規範翻譯工做 [3] RTMP 協議規範 1.0 中文版
「視頻雲技術」你最值得關注的音視頻技術公衆號,每週推送來自阿里雲一線的實踐技術文章,在這裏與音視頻領域一流工程師交流切磋。