天天看點

流媒體傳輸協定之 RTMP

引言

本系列文章将整理各路流媒體傳輸協定,包括RTP/RTCP,RTMP,希望通過深入了解各個流媒體傳輸協定的設計細節,對今後流媒體部分的開發工作有一定的啟發。

簡介

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之後才可以發送其他資料。

C0和S0格式

C0和S0是單獨的一個位元組,可以當做一個8bit的整數字段來對待。

流媒體傳輸協定之 RTMP

以下是CO和S0包的字段解釋:

  • 版本号(8位): 在C0包中,該字段表示用戶端請求的RTMP版本。在S0中,該字段表示伺服器選擇的RTMP版本。本規範所定義的版本是3。可選值中,0-2是早期版本所用的,已被丢棄,4-31保留在未來使用,32-255不允許使用(為了區分其他以某一可見字元開始的文本協定)。如果伺服器不能識别用戶端請求的版本,應該傳回3,用戶端可能選擇降級到版本3,也可能放棄握手。

C1和S1格式

C1和S1包固定為1536位元組,包含以下字段:

流媒體傳輸協定之 RTMP
  • 時間戳(4位元組):該字段承載一個時間戳,該時間戳應該作為發送端點所有後續塊的時間戳起始時間。可以是0,也可以是其他的任意值。為了同步多個塊流,端點可能會發送其他塊流的目前時間戳。
  • 零值(4位元組):該字段所有值都必須為0。
  • 随機資料(1528位元組):該字段可以是任意值。通過這個字段來區分自己和連接配接的另一方,是以此資料應該有充分的随機性,但是沒必要使用加密安全的随機值或動态值。

C2和S2格式

C2和S2包的長度也為1536位元組,基本上是S1和C1的回傳,包含以下字段:

流媒體傳輸協定之 RTMP
  • 時間戳(4位元組):該字段必須包含對端發來的時間戳(對C2來說是S1, 對S2來說是C1)。
  • 時間2(4位元組):該字段必須包含先前發送的并被對端讀取的包的時間戳。(對C2來說是C1,對S2來說是S1)。
  • 随機資料回顯(1528位元組):該字段必須包含對端發送過來的随機資料字段值(對C2來說是S1, 對S2來說是C1)。任何一端都可以用時間戳和時間戳2兩個字段值和目前時間戳來快速的估算帶寬和延遲,但這樣可能并不實用。

握手流程示意圖

流媒體傳輸協定之 RTMP

上圖提到的狀态的解釋如下:

  • Uninitialized:未初始化狀态。在該階段發送協定版本。用戶端在C0包中發送RTMP協定版本,如果伺服器支援此版本,伺服器将在響應中發送S0和S1。如果不支援,伺服器采用适當的行為作為響應,在RTMP規範中是終止連接配接。
  • Version Send:版本已發送狀态。在未初始化狀态之後用戶端和服務端都進入版本已發送狀态。用戶端等待接收S1包,服務端等待接收C1包。收到所等待的包後,用戶端發送C2包,服務端發送S2包。之後狀态進入發送确認狀态。
  • Ack Send:用戶端和服務端等待接收S2和C2包,收到後進入握手完成狀态。
  • Handshake Done:握手完成, 用戶端和服務端開始交換消息。

分塊

握手完成後,一個或多個塊流可能會複用同一個連接配接,每個塊流承載來自同一個消息流的同一類消息。每個塊都有一個唯一的塊流ID,這些塊通過網絡進行傳輸。在傳輸過程中,必須一個塊發送完畢之後再發送下一個塊。在接收端,将所有塊根據塊中的塊流ID組裝成消息。

分塊将上層協定的大消息分割成小的消息,保證大的低優先級消息(比如視訊)不阻塞小的高優先級消息(比如音頻或控制消息)。

分塊還能降低消息發送的開銷,它在塊頭中包含了壓縮的原本需要在消息中所包含的資訊。

塊大小是可配置的,這個可以通過一個設定塊大小控制消息進行設定修改。越大的塊CPU使用率越低,但是在低帶寬的情況下,大的寫入會阻塞其他内容的寫入。而小一些的塊不适合高比特率的流。

塊格式

每個塊由塊頭和資料組成,塊頭包含3部分:基本頭、消息頭和擴充時間戳。

流媒體傳輸協定之 RTMP
  • 基本頭(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個位元組來編碼:

流媒體傳輸協定之 RTMP

64-319範圍内的塊流ID用2個位元組來編碼,塊流ID為計算所得,公式為:第二個位元組值 + 64:

流媒體傳輸協定之 RTMP

64-65599範圍内的塊流ID用3個位元組來編碼,塊流ID為計算所得,公式為:第三個位元組值*255 + 第二個位元組值 + 64

流媒體傳輸協定之 RTMP

上述圖中各個部分的含義如下:

  • 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個位元組長度,該類型必須用在一個塊流的開頭,和每當塊流時間戳回退的時候(例如視訊回退的操作)。

流媒體傳輸協定之 RTMP
  • 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。對于傳輸大小可變消息的流(如多數視訊格式),在發送第一個消息之後的每個消息都應該使用該類型格式。

流媒體傳輸協定之 RTMP
  • timestamp delta(3位元組): 時間戳增量。類型1和類型2的塊包含此字段,表示前一個塊的timestamp字段和目前塊timestamp間的內插補點。 如果時間戳增量大于或等于16777215(0xFFFFFF),該字段必須為16777215,并且必須設定擴充時間戳,來共同表示32位的時間戳增量,否則該字段值就是實際的時間戳增量。

類型2

2類型的塊消息頭占用3個位元組長度,不包含消息流ID和消息長度,沿用上一個塊的消息流ID和消息長度。對于傳輸固定大小消息的流(如音頻和資料格式),在發送第一個消息之後的每一個消息都應該使用該類型格式。

流媒體傳輸協定之 RTMP

類型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

這是一個簡單的音頻流消息,這是示例示範了資訊備援。

流媒體傳輸協定之 RTMP

下圖展示該消息流以塊流形式發送。從3類型塊開始了資料傳輸優化,之後的塊隻附加了一個位元組。

流媒體傳輸協定之 RTMP
示例2

該示例展示了一個超過128位元組長度的消息,消息被分割成了數個塊。

流媒體傳輸協定之 RTMP

下圖是被分割成的塊:

流媒體傳輸協定之 RTMP

第一個塊的頭資訊指明了消息總大小為307位元組。

注意這兩個示例,3類型塊可以在兩種情況下使用。第一種情況是消息拆分成多個塊,另一種情況是新消息複用上一個消息的所有頭部内容。

協定控制消息

RTMP塊流用消息類型1,2,3,5和6來作為協定控制消息,這些消息包含RTMP塊流協定所需要的資訊。

這些協定控制消息必須用0作為消息流ID(控制流ID),并在ID為2的塊流中發送。協定控制消息收到後立即生效,它們的時間戳資訊是被忽略的。

設定塊大小

協定控制消息類型1:設定塊大小,用于通知另一端新的最大塊大小。

最大塊大小預設為128位元組,用戶端或服務端可以修改此值,并用該消息通知另一端。例如,假設一個用戶端想要發送131位元組的音頻資料,而最大塊大小為128。在這種情況下,用戶端可以向服務端發送該消息,通知它最大塊大小被設定為了131位元組。這樣用戶端隻用一個塊就可以發送這些音頻資料。

最大塊大小不能小于1位元組,通常應該不低于128位元組。每個方向上的最大塊大小是獨立的。

流媒體傳輸協定之 RTMP
  • 0(1位): 該位必須為0.
  • chunk size(31位): 該字段以位元組形式儲存新的最大塊大小,該值将用于後續的所有塊的發送,直到收到新的通知。該值可取值範圍為1-2147483647(0x7FFFFFFF),但是所有大于1677215(0xFFFFFF)的值都是視作是16777215,因為任何塊不可能比消息大,而消息長度不能大于16777215位元組。

終止消息

協定控制消息類型2:終止消息,通知正在等待消息後續塊的另一端,可以丢棄指定塊流接收到的資料,塊流ID為該消息的載荷。應用可能在關閉的時候發送該消息,用來表明後面的消息沒有必要繼續處理了。

流媒體傳輸協定之 RTMP
  • chunk stream id(32位元組): 指定消息的塊流ID。

确認消息

用戶端或伺服器在收到資料總長和視窗大小相等時,通過它回複确認消息。在連接配接建立完成後,消息的發送方會通知接收方一個視窗的大小(指定一個長度),如果接收方收到指定長度的資料後沒有發送回複消息,發送方就不會再發送任何内容了。

流媒體傳輸協定之 RTMP
  • sequence number(32位元組): 到目前時刻為止接收到的位元組總數。

确認視窗大小

用戶端或服務端發送該消息來通知對端發送确認消息所使用的視窗大小,并等待接收端發送确認消息。接收端在接收到視窗大小後必須發送确認消息。

流媒體傳輸協定之 RTMP

設定對方傳輸帶寬

用戶端或服務端發送該消息來限制對端的輸出帶寬。接收端收到消息後,可以直接使用消息中指定的視窗大小,而不需要等待收到确認消息之後。如果視窗大小與上一個視窗大小不同,則該消息的接收端應該向該消息的發送端發送新的視窗大小消息。這個消息和上一個消息都是調整視窗大小的,不同的地方是,這個消息是接受者請求發送者,讓它調整視窗大小,而上一個消息是發送者主動設定了視窗大小,通知資料接受者。

流媒體傳輸協定之 RTMP

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

消息有效負載

消息的另一部分就是有效負載,也是消息包含的實際資料,可以是音頻樣本或者壓縮的視訊資料。

使用者控制消息

RTMP協定将消息類型4作為使用者控制消息ID,這些消息包含RTMP流所需的必要資訊。消息類型1,2,3,5和6由RTMP塊流協定使用。

使用者控制消息應該使用ID為0的消息流(控制流),并且通過RTMP塊流傳輸時使用ID為2的塊流。使用者控制消息收到後立即生效,它們的時間戳資訊會被忽略。

用戶端或服務端通過發送該消息告知對方使用者控制事件。該消息攜帶事件類型和事件資料兩部分。

流媒體傳輸協定之 RTMP

開頭的2個位元組用于指定事件類型,緊跟着是事件資料。事件資料字段長度可變,但是如果用RTMP塊流傳輸,則消息總長度不能超過最大塊大小,以使消息可以使用一個單獨的塊進行傳輸。

RTMP指令消息

各種類型的消息在用戶端和服務端之間進行交換,包括用于發送音頻資料的音頻消息,用于發送視訊資料的視訊消息,用于發送任意使用者資料的資料消息,共享對象消息和指令消息等。共享對象消息的主要用途是管理用戶端和相同伺服器的共享資料。指令消息發送的是用戶端與服務端之間的AMF編碼指令,用戶端或服務端也可以通過指令消息來實作遠端過程調用(RPC)。

消息類型

用戶端和服務端通過在網絡上發送消息來實作互動,消息可以是任意類型,包括音頻消息、視訊消息、指令消息、共享對象消息、資料消息和使用者控制消息等。

指令消息

指令消息在用戶端和服務端之間傳遞AMF編碼的指令,消息類型20代表AMF0編碼,消息類型17代表AMF3編碼。發送這些消息來完成連接配接、建立流、釋出、播放、暫停等操作。像狀态、結果這樣的指令消息,用于通知發送方請求的指令狀态。一條指令消息由指令名、事務ID和包含相關參數的指令對象組成。用戶端或服務端還可以通過指令消息來實作遠端過程調用(RPC)。

資料消息

用戶端或服務端通過該消息來發送中繼資料或其他使用者資料。中繼資料包括資料(音頻、視訊)的建立時間、時長、主題等詳細資訊。消息類型18代表AMF0編碼,消息類型15代表AMF3編碼。

共享對象消息

共享對象是在多個用戶端之間同步的Flash對象(鍵值對集合)。消息類型19代表AMF0編碼,消息類型16代表AMF3編碼。每個消息都可以包含多個事件。

流媒體傳輸協定之 RTMP

支援以下事件類型:

  • 建立(1):用戶端向服務端發送,請求建立指定名稱的共享對象。
  • 釋放(2):用戶端通知服務端,共享對象已在本地删除。
  • 請求更新(3):用戶端請求修改共享對象的屬性值。
  • 更新(4):通知服務端向除自己外的其他用戶端發送共享資料消息,通知它們有屬性的值發生了變化。
  • 成功(5):“請求更新”事件被接受後,服務端向發送請求的用戶端回複此事件。
  • 發送消息(6):用戶端向服務端發送此事件,來廣播一個消息。服務端收到此事件後向所有用戶端廣播一條消息,包括請求方用戶端。
  • 狀态(7):服務端發送此事件來通知用戶端錯誤資訊。
  • 清除(8):服務端向用戶端發送此事件,通知用戶端清除一個共享對象。服務端在回複用戶端的“建立”事件時也會發送此事件。
  • 移除(9):服務端發送此事件,使用戶端删除一個插槽。
  • 請求移除(10):用戶端删除一個插槽時發送此事件。
  • 建立成功(11):當連接配接成功時服務端向用戶端發送此事件。

音頻消息

用戶端或服務端通過發送此消息來發送音頻資料給對方,消息類型8是為音頻消息預留的。

視訊消息

用戶端或服務端通過發送此消息來發送視訊資料給對方,消息類型9是為視訊消息預留的。

組合消息

組合消息,是一個消息包含多個子RTMP消息,子消息符合RTMP消息格式。消息類型22用于組合消息。

流媒體傳輸協定之 RTMP

組合消息的消息流ID會覆寫其中子消息的消息流ID。

組合消息的時間戳和其中第一個子消息的時間戳的內插補點,是用來将所有子消息的時間戳重整為流時間的位移量。位移量會加到每一個子消息的時間戳上來換算出正常的流時間。第一個子消息的時間戳應該與組合消息的時間戳相同,是以位移量應該為0。

Back Pointer(反向指針)包含前一個消息的長度(包括消息頭),這樣符合flv檔案格式,可用于進行後退操作。

使用組合消息有以下好處:

  • 塊流協定中,一個塊最多隻能發送一個消息,這樣就使用組合消息,加大塊大小,進而降低發送的塊數量。
  • 子消息在記憶體中連續存放,這樣系統調用網絡發送資料的性能更高。

使用者控制消息事件

用戶端或伺服器通過該消息發送使用者控制事件。

流媒體傳輸協定之 RTMP

使用者控制消息支援以下事件:

  • 流開始(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指令至伺服器端以請求連接配接至某一伺服器程式執行個體。

指令結構如下:

流媒體傳輸協定之 RTMP

Connect指令中會用到的鍵值對:

流媒體傳輸協定之 RTMP

音頻編碼:

流媒體傳輸協定之 RTMP

視訊編碼:

流媒體傳輸協定之 RTMP

視訊功能:

流媒體傳輸協定之 RTMP

對象編碼:

流媒體傳輸協定之 RTMP

由伺服器發送至用戶端的指令結構如下:

流媒體傳輸協定之 RTMP

指令執行流程:

流媒體傳輸協定之 RTMP

指令執行的消息流如下:

  • 用戶端發送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)等。

NetConnection對象的call方法用于遠端調用接收端上的程式。需要遠端調用的程式名稱通過一個參數傳遞給call指令。

發送指令結構如下:

流媒體傳輸協定之 RTMP

響應指令結構如下:

流媒體傳輸協定之 RTMP

用戶端發送該指令至伺服器端以建立一條用于傳遞消息的邏輯通道,進而可以利用已建立的流通道釋出音頻、視訊和中繼資料。

NetConnection是預設的通訊通道,流ID為0。協定和一些指令消息,包括createStream,使用預設通訊通道。

用戶端發出的指令結構如下:

流媒體傳輸協定之 RTMP

伺服器發出的指令結構如下:

流媒體傳輸協定之 RTMP

NetStream指令

基于NetConnection的用戶端至伺服器間連接配接,NetStream定義了一條可以傳遞音頻流、視訊流以及消息流的通道。NetConnection對象支援多個NetStreams以傳輸多個資料流。

用戶端可在NetStream中發送下列指令至伺服器:

  • Play
  • Play2
  • DeleteStream
  • CloseStream
  • ReceiveAudio
  • ReceiveVideo
  • Publish
  • Seek
  • Pause

伺服器端通過“onStatus"将NetStream的狀态更新至用戶端:

流媒體傳輸協定之 RTMP

用戶端發送該指令值以播放一個流。多次調用該指令也可建立一個播放清單。

如果你希望建立一個在不同live或recorded流間切換的動态播放清單,需要多次調用play并傳遞false以避免每次reset。相反地,如果你希望立即播放某一指定流,傳遞true以清除等待播放隊列中的所有其他流。

用戶端發送的指令結構如下:

流媒體傳輸協定之 RTMP

流程圖如下:

流媒體傳輸協定之 RTMP

指令執行期間的消息流如下:

  • 用戶端在接收到來自伺服器的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。随後,伺服器就發送用戶端播放的音頻和視訊資料。

不同于play指令,play2可以切換碼率而不改變播放内容的時間軸。伺服器為用戶端可以在play2中請求的所有支援的碼率維護多個字段。

流媒體傳輸協定之 RTMP

該指令的消息流程如下圖:

流媒體傳輸協定之 RTMP

當NetStream對象将要被銷毀時,它發送該deleteStream指令。

流媒體傳輸協定之 RTMP

伺服器不需要發送任何應答。

NetStream發送ReceiveAudio消息通知伺服器是否發送或不發送音頻到用戶端。

流媒體傳輸協定之 RTMP

如果receiveAudio指令發送帶有flase的bool flag,伺服器不發送任何響應。如果這個标志被設定為true,伺服器應答NetStream.Seek.Notify和NetStream.Play.Start的狀态消息。

NetStream發送ReceiveVideo消息通知伺服器是否發送或不發送視訊到用戶端。

流媒體傳輸協定之 RTMP

如果receiveVideo指令發送帶有flase的bool flag,伺服器不發送任何響應。如果這個标志被設定為true,伺服器應答NetStream.Seek.Notify和NetStream.Play.Start的狀态消息。

用戶端發送publish指令将已命名的流釋出到伺服器上。使用這個名稱,任何用戶端都可以播放此流,并接收已釋出的音頻、視訊和資料消息。

流媒體傳輸協定之 RTMP

伺服器應答onStatus指令,以标記釋出的開始。

用戶端發送seek指令以定位媒體檔案内或者播放清單的某個位置(以毫秒為機關)。

流媒體傳輸協定之 RTMP

當定位成功,伺服器發送NetStream.Seek.Notify的狀态消息。失敗的時候,它傳回一個_error的消息。

用戶端發送pause指令以告訴伺服器暫停或者開始播放。

流媒體傳輸協定之 RTMP

當流被暫停,伺服器發送一個NetStream.Pause.Notify的狀态消息。當一個流變成未暫停狀态,NetStream.Unpause.Notify被發送。失敗的時候,它傳回一個_error的消息。

消息交換例子

這裡是一些樣例,以解釋使用RTMP的消息交換。

釋出視訊

這個例子說明了一個釋出者如何可以釋出一個流,然後将視訊推流到伺服器上。其他用戶端可以訂閱這個已釋出的流,并播放視訊。

流媒體傳輸協定之 RTMP

廣播一個共享對象消息

這個例子說明了在建立和更改共享對象時所交換的消息。它也說明了共享對象消息廣播的過程。

流媒體傳輸協定之 RTMP

釋出媒體流中繼資料

這個例子描述了釋出中繼資料的消息交換。

流媒體傳輸協定之 RTMP

文章說明

更多有價值的文章均收錄于

貝貝貓的文章目錄
流媒體傳輸協定之 RTMP

版權聲明: 本部落格所有文章除特别聲明外,均采用 BY-NC-SA 許可協定。轉載請注明出處!

創作聲明: 本文基于下列所有參考内容進行創作,其中可能涉及複制、修改或者轉換,圖檔均來自網絡,如有侵權請聯系我,我會第一時間進行删除。

參考内容

[1]

RTMP 規範

[2]

RTMP協定規範翻譯工作

[3]

RTMP協定規範1.0中文版

繼續閱讀