RTMP協(xié)議:直播推流協(xié)議

一反惕、RTMP 協(xié)議簡(jiǎn)介

RTMP(Real-Time Messaging Protocol),譯為:實(shí)時(shí)消息傳輸協(xié)議,它是由 Adobe 公司提出的一種基于 TCP 的應(yīng)用層協(xié)議,用來(lái)解決多媒體數(shù)據(jù)傳輸流的多路復(fù)用(Multiplexing)和分包(Packetizing)的問(wèn)題蕉扮。RTMP 在兩個(gè)對(duì)等的通信端之間通過(guò)可靠的傳輸協(xié)議提供雙向的消息多路服務(wù),用來(lái)傳輸帶有時(shí)間信息的并行的視頻颗圣、音頻和數(shù)據(jù)喳钟。通常的協(xié)議的實(shí)現(xiàn)會(huì)給不同類(lèi)型的消息賦予不同的優(yōu)先級(jí),當(dāng)傳輸能力受到限制時(shí)它會(huì)影響消息下層流發(fā)送的隊(duì)列順序在岂。由于協(xié)議設(shè)計(jì)對(duì)低延時(shí)奔则、音視頻同步等能力的良好支持,RTMP 是實(shí)時(shí)直播場(chǎng)景蔽午,尤其是在推流上行鏈路中易茬,最常用的傳輸協(xié)議之一。

二及老、塊流(RTMP Chunk Stream)

RTMP Chunk Stream 為配合 RTMP 協(xié)議而設(shè)計(jì)抽莱,主要為 RTMP 提供復(fù)用和分包的功能。每個(gè)消息包含時(shí)間戳和負(fù)載類(lèi)型信息骄恶。RTMP Chunk Stream 為配合 RTMP 協(xié)議而設(shè)計(jì)食铐, 它也可以服務(wù)于任何發(fā)送消息流的協(xié)議。 每個(gè)消息包含時(shí)間戳和負(fù)載類(lèi)型信息僧鲁。RTMP Chunk Stream 除自身內(nèi)置的協(xié)議控制消息外虐呻, 還為上層協(xié)議提供了用戶控制消息的機(jī)制象泵。RTMP Chunk Stream 和 RTMP 適合各種音視頻應(yīng)用, 不管是一對(duì)一還是一對(duì)多場(chǎng)景都能很好的滿足铃慷。 RTMP Chunk Stream 可以理解為是對(duì)傳輸 RTMP Chunk 的流的邏輯上的抽象单芜,客戶端和服務(wù)器之間有關(guān) RTMP 的信息都在這個(gè)流上通信蜕该。

三犁柜、消息(RTMP Message)

1、RTMP 消息格式(RTMP Message Format)

RTMP 消息是 RTMP 協(xié)議中基本的數(shù)據(jù)單元堂淡。服務(wù)端和客戶端通過(guò)在網(wǎng)絡(luò)上發(fā)送消息實(shí)現(xiàn)之間的交互馋缅,消息包括但不限于音頻、視頻绢淀、數(shù)據(jù)萤悴。這里的消息是指滿足該協(xié)議格式的、可以切分成 Chunk 發(fā)送的消息皆的。RTMP 消息包含兩部分:消息頭(Message Header)和有效數(shù)據(jù)(Message Body)覆履。消息頭(Message Header)包含以下信息:

Message Type:消息類(lèi)型,占用 1 個(gè)字節(jié)费薄。1~6 的消息類(lèi)型 ID 是為協(xié)議控制消息保留的硝全。
Length:有效負(fù)載的字節(jié)數(shù),即音視頻等信息的數(shù)據(jù)長(zhǎng)度楞抡。占用 3 個(gè)字節(jié)伟众,采用大端存儲(chǔ)(big-endian)模式。
Timestamp:時(shí)間戳召廷,占用 4 個(gè)字節(jié)凳厢,采用大端存儲(chǔ)模式。
Message Stream ID:消息流 ID竞慢,標(biāo)識(shí)消息所使用的流先紫,采用大端存儲(chǔ)模式。

RTMP Message Header

2筹煮、分塊(Chunking)

RTMP 在收發(fā)數(shù)據(jù)時(shí)并不是以 Message 為單位的泡孩,而是把 Message 拆分成 Message Chunk 發(fā)送,被拆分的每個(gè) Message Chunk 都有一個(gè)唯一的 ID寺谤, 最后在接收端會(huì)按照 Message Chunk ID 將 Message Chunk 重新組裝成 Message仑鸥。在傳輸時(shí)必須在一個(gè) Message Chunk 發(fā)送完成之后才能開(kāi)始發(fā)送下一個(gè)。把數(shù)據(jù)量較大的 Message 拆分成較小的 Message Chunk变屁,在傳輸時(shí)可以避免優(yōu)先級(jí)低的消息持續(xù)發(fā)送阻塞優(yōu)先級(jí)高的數(shù)據(jù)眼俊,比如 Message 中的數(shù)據(jù)會(huì)包括視頻幀、音頻幀和控制信息粟关,如果持續(xù)發(fā)送音頻數(shù)據(jù)或者控制數(shù)據(jù)的話可能就會(huì)造成視頻幀的阻塞疮胖,然后就會(huì)造成看視頻時(shí)最煩人的卡頓現(xiàn)象。同時(shí)對(duì)于數(shù)據(jù)量較小的 Message,可以通過(guò)對(duì) Chunk Header 的字段來(lái)壓縮信息澎灸,從而減少信息的傳輸量院塞。

Message Chunk 的默認(rèn)大小是 128 字節(jié),在傳輸過(guò)程中性昭,通過(guò) Set Chunk Size 可以設(shè)置 Message Chunk 數(shù)據(jù)量的最大值拦止,在發(fā)送端和接受端會(huì)各自維護(hù)一個(gè) Chunk Size,可以分別設(shè)置這個(gè)值來(lái)改變自己這一方發(fā)送的 Chunk 的最大大小糜颠。大一點(diǎn)的 Message Chunk 減少了計(jì)算每個(gè) Message Chunk 的時(shí)間從而減少了 CPU 的占用率汹族,但是它會(huì)占用更多的時(shí)間在發(fā)送上,尤其是在低帶寬的網(wǎng)絡(luò)情況下其兴,很可能會(huì)阻塞后面更重要信息的傳輸顶瞒。小一點(diǎn)的 Message Chunk 可以減少這種阻塞問(wèn)題,但小的 Message Chunk 會(huì)引入過(guò)多額外的信息(Chunk 中的 Header)元旬,少量多次的傳輸也可能會(huì)造成發(fā)送的間斷導(dǎo)致不能充分利用高帶寬的優(yōu)勢(shì)榴徐,因此并不適合在高比特率的流中傳輸。在實(shí)際發(fā)送時(shí)應(yīng)對(duì)要發(fā)送的數(shù)據(jù)用不同的 Chunk Size 去嘗試匀归,通過(guò)抓包分析等手段得出合適的 Message Chunk 大小坑资,并且在傳輸過(guò)程中可以根據(jù)當(dāng)前的帶寬信息和實(shí)際信息的大小動(dòng)態(tài)調(diào)整 Message Chunk 的大小,從而盡量提高 CPU 的利用率并減少信息的阻塞機(jī)率朋譬。

3盐茎、RTMP 消息塊格式(RTMP Message Chunk Format)

Message Chunk 由塊頭(Chunk Header)和數(shù)據(jù)(Chunk Data)組成,Chunk Header包含 3 部分:基本頭(Basic Header)徙赢、消息頭(Message Header)和擴(kuò)展時(shí)間戳(Extended Timestap)字柠。

RTMP Message Chunk Format

3.1、基本頭(Basic Header)

該部分編碼Chunk Stream ID(流通道 ID狡赐,簡(jiǎn)稱(chēng) CSID) 和 Chunk Type(下圖中fmt字段)窑业。基本頭的長(zhǎng)度是 1~3 字節(jié)枕屉,采用小端存儲(chǔ)模式常柄,其長(zhǎng)度取決于CSIDCSID是一個(gè)變長(zhǎng)字段搀擂,用來(lái)唯一標(biāo)識(shí)一個(gè)特定的流通道西潘。Chunk Type 決定了消息頭(Message Header)的編碼格式,長(zhǎng)度固定占 2 位(bit)哨颂。在足夠存儲(chǔ)這兩個(gè)字段的前提下應(yīng)該用盡可能少的字節(jié)來(lái)表示喷市,這樣能夠減少由于引入 Header 增加的數(shù)據(jù)量。

RTMP 最多支持 65597 個(gè)流威恼,CSID3~65599范圍內(nèi)品姓。CSID0寝并,12為保留值腹备。0表示塊基本頭為 2 個(gè)字節(jié)衬潦,并且CSID范圍在64~319之間(第二個(gè)字節(jié) + 64);1表示塊基本頭為 3 個(gè)字節(jié)植酥,并且 ID 范圍在64~65599之間(第三個(gè)字節(jié) * 256 + 第二個(gè)字節(jié) + 64)镀岛;取值在 3~63 之間的 ID 表示完整的CSID。值2是為低版本協(xié)議保留的惧互,用于協(xié)議控制消息和命令哎媚,第0~5位(不重要的)表示CSID喇伯。

當(dāng)Basic Header長(zhǎng)度為 1 個(gè)字節(jié)時(shí)喊儡,CSID占 6 位(6 位表示的范圍為0~63),由于CSID的 0稻据,1艾猜,2 為保留值,因此用戶可以定義的CSID范圍為3~63捻悯,此時(shí)的 CSID是完整的匆赃,不需要計(jì)算得出:

Chunk Basic Header - 1 字節(jié)

當(dāng)Basic Header長(zhǎng)度為 2 個(gè)字節(jié)時(shí),CSID占 14 位今缚,此時(shí) RTMP 將與 Chunk Type 所在字節(jié)的其他位都置為 0算柳,剩下的 1 個(gè)字節(jié)來(lái)表示CSID - 64,這樣共有 8 位(8 位表示的范圍為 0~255)來(lái)存儲(chǔ)CSID姓言,CSID計(jì)算公式:CSID = 第二個(gè)字節(jié)的值 + 64瞬项,因此計(jì)算得到CSID范圍是64~319

Chunk Basic Header - 2 字節(jié)

當(dāng)Basic Header長(zhǎng)度為 3 個(gè)字節(jié)時(shí),CSID占 22 位何荚,此時(shí) RTMP 將與 Chunk Type 所在字節(jié)的其他位都置為 1囱淋,剩下的 16 位表示CSID - 64,這樣共有 16 位(16 位表示的范圍為0~65535)來(lái)存儲(chǔ)CSID餐塘,因此計(jì)算得到 CSID 范圍是64~65599妥衣。CSID計(jì)算公式為:第三個(gè)字節(jié)值*255 + 第二個(gè)字節(jié)值 + 64.。 64~319 范圍內(nèi)的SCID可以用 2 字節(jié)長(zhǎng)度來(lái)編碼戒傻,也可以用 3 字節(jié)長(zhǎng)度來(lái)編碼税手。但實(shí)際實(shí)現(xiàn)時(shí)還是應(yīng)該秉著最少字節(jié)的原則使用 2 個(gè)字節(jié)的表示方式來(lái)表示范圍為64~319CSID

Chunk Basic Header - 3 字節(jié)

3.2、消息頭(Message Header)

Message Header 共有 4 種不同的格式需纳,Message Header的格式和長(zhǎng)度取決于 Basic Header 中的 Chunk Type(即fmt字段值)芦倒。協(xié)議實(shí)現(xiàn)方應(yīng)該用最緊湊的格式來(lái)表示塊消息頭。該部分編碼所發(fā)送消息的描述信息(無(wú)論是整個(gè)消息還是一部分)候齿。該部分的長(zhǎng)度可能是 0 字節(jié)熙暴、3 字節(jié)闺属、7 字節(jié)或者 11 字節(jié),其長(zhǎng)度取決于基本頭中指定的 Chunk Type周霉。

3.2.1掂器、塊類(lèi)型 0(Type 0)

0 類(lèi)型的塊消息頭占 11 個(gè)字節(jié)長(zhǎng)度。在 Chunk Stream 發(fā)送第一個(gè) Chunk 時(shí)和Message Header中的時(shí)間戳后退(即當(dāng)前 Chunk 的時(shí)間戳小于上一個(gè) Chunk 的時(shí)間戳俱箱,回退播放時(shí)會(huì)出現(xiàn)這種情況)時(shí)必須使用0 類(lèi)型的 Chunk国瓮。

Chunk Message Header - Type 0

timestamp(3 字節(jié)):0 類(lèi)型的 Chunk 的絕對(duì)時(shí)間戳。 如果時(shí)間戳大于等于16777215(0xFFFFFF)狞谱,該字段的值必須為16777215乃摹,即 3 個(gè)字節(jié)全部置為 1,此時(shí)實(shí)際的 timestamp 會(huì)轉(zhuǎn)存到 Chunk 的Extended Timestamp字段中跟衅,接受端在判斷 timestamp 字段 24 個(gè)位都為 1 時(shí)就會(huì)去Extended Timestamp中解析實(shí)際的時(shí)間戳孵睬。
message length(3 字節(jié)): 消息長(zhǎng)度, 0 類(lèi)型和 1 類(lèi)型的 Chunk 包含此字段伶跷,表示消息長(zhǎng)度掰读,即當(dāng)前 Chunk 所屬消息的數(shù)據(jù)總長(zhǎng)度,而非當(dāng)前 Chunk 的數(shù)據(jù)長(zhǎng)度叭莫。除了最后一個(gè) Chunk蹈集,其他 Chunk 的數(shù)據(jù)長(zhǎng)度大小都等于 Chunk Size 大小。
message type id(1 字節(jié)):消息類(lèi)型 ID(數(shù)據(jù)的類(lèi)型)雇初,如 8 代表音頻數(shù)據(jù)拢肆、9 代表視頻數(shù)據(jù)。
message stream id(4 字節(jié)):消息流 ID靖诗,表示該 Chunk 所在流的 ID郭怪,采用小端存儲(chǔ)模式。通常呻畸,相同 Chunk Stream 中的消息屬于用一個(gè) Message Stream移盆。雖然不同的 Message Stream 復(fù)用相同的 Chunk Stream 會(huì)導(dǎo)致Message Header無(wú)法有效壓縮,但是當(dāng)一個(gè) Message Stream 已關(guān)閉伤为,才打開(kāi)另外一個(gè) Message Stream咒循,就可以通過(guò)發(fā)送一個(gè)新的0 類(lèi)型 Chunk 來(lái)實(shí)現(xiàn)復(fù)用。

3.2.2绞愚、塊類(lèi)型 1(Type 1)

1 類(lèi)型的塊消息頭占用 7 個(gè)字節(jié)長(zhǎng)度叙甸,不包含message stream id,表示和上一個(gè) Chunk 的message stream id 是相同的位衩。對(duì)于傳輸大小可變消息的流(如多數(shù)視頻格式)裆蒸,在發(fā)送第一個(gè)消息之后的每個(gè)消息,第一個(gè)塊都應(yīng)該使用該類(lèi)型格式糖驴。

Chunk Message Header - Type 1

timestamp delta(3字節(jié)): 時(shí)間戳增量僚祷。 1 類(lèi)型2 類(lèi)型的 Chunk 包含此字段佛致,存儲(chǔ)的是當(dāng)前 Chunk 的timestamp和上一個(gè) Chunk 的timestamp差值。當(dāng)它的值超過(guò) 3 個(gè)字節(jié)所能表示的最大值時(shí)辙谜,三個(gè)字節(jié)都置為 1俺榆,實(shí)際的時(shí)間戳差值就會(huì)轉(zhuǎn)存到Extended Timestamp字段中,接受端在判斷timestamp delta字段 24 個(gè)位都為 1 時(shí)就會(huì)去Extended timestamp中解析實(shí)際的與上次時(shí)間戳的差值装哆。
message length(3 字節(jié)): 同0 類(lèi)型message length罐脊。
message type id(1 字節(jié)):同0 類(lèi)型message type id

3.2.3蜕琴、塊類(lèi)型 2(Type 2)

2 類(lèi)型的塊消息頭占用 3 個(gè)字節(jié)長(zhǎng)度萍桌,僅包timestamp delta(同1 類(lèi)型message delta),表示當(dāng)前 Chunk 和上一次發(fā)送的 Chunk 的 message length凌简、 message type idmessage stream id都相同上炎。對(duì)于傳輸固定大小消息的流(如音頻和數(shù)據(jù)格式),在發(fā)送第一個(gè)消息之后的每一個(gè)消息号醉,第一個(gè)塊都應(yīng)該使用該類(lèi)型格式反症。

Chunk Message Header - Type 2

3.2.4辛块、塊類(lèi)型 3(Type 3)

3 類(lèi)型的 Chunk 占用 0 字節(jié)畔派,也就是沒(méi)有消息頭,表示當(dāng)前 Chunk 的 Message Header 和上一個(gè) Chunk 的 Message Header 是相同的润绵。

而當(dāng)它跟在1 類(lèi)型或者2 類(lèi)型的 Chunk 后面時(shí)线椰,表示和上一個(gè) Chunk 的timestamp delta是相同的。比如第一個(gè)0 類(lèi)型 Chunk 的timestamp=1000尘盼;第二個(gè)2 類(lèi)型 Chunk 的timestamp delta= 20憨愉,則時(shí)間戳為1000 + 20 = 1020;第三個(gè)3 類(lèi)型 Chunk 的timestamp delta= 20卿捎,則時(shí)間戳為 1020 + 20 = 1040配紫;

Multiple Messages

下圖中展示了上圖中消息流分割成的塊:


Chunks

當(dāng)它跟在0 類(lèi)型的 Chunk 后面時(shí),表示和上一個(gè) Chunk 的時(shí)間戳是相同的午阵。這種情況出現(xiàn)在一個(gè) Message 被拆分成了多個(gè) Chunk 時(shí)躺孝,當(dāng)前 Chunk 和上一個(gè) Chunk 同屬于一個(gè) Message 的情況下。如下圖:

Single Message

下圖是被分割成的塊:


Chunks

4底桂、擴(kuò)展時(shí)間戳(Extended Timestamp)

前面提到在0 類(lèi)型 Chunk 的Message Header中會(huì)有時(shí)間戳timestamp植袍,以及1 類(lèi)型2 類(lèi)型 Chunk 的Message Header中有時(shí)間戳差timestamp delta,當(dāng)timestamp或者timestamp delta大于 3 個(gè)字節(jié)所能表示的最大數(shù)值167772150xFFFFFF)時(shí)籽懦,才會(huì)用到這個(gè)字段于个,否則這個(gè)字段值為 0。擴(kuò)展時(shí)間戳占 4 個(gè)字節(jié)暮顺,能表示的最大數(shù)值就是42949672950xFFFFFFFF)厅篓。當(dāng)Extended Timestamp啟用時(shí)秀存,timestamp或者timestamp delta要全置為1,表示應(yīng)該去擴(kuò)展時(shí)間戳字段來(lái)提取真正的時(shí)間戳或者時(shí)間戳差羽氮。擴(kuò)展時(shí)間戳存儲(chǔ)的是完整值应又,而不是減去時(shí)間戳或者時(shí)間戳差的值。

三乏苦、協(xié)議控制消息(Protocol Control Messages)

RTMP Chunk Stream 用Message Type1株扛,23汇荐,56的 Message 來(lái)作為協(xié)議控制消息洞就,這些 Message 包含 RTMP Chunk Stream Protocol 所需要的信息,所以協(xié)議控制消息是在 RTMP Chunk Stream Protocol 層的消息掀淘。在 Chunk Stream 中發(fā)送時(shí)旬蟋,Message Stream ID = 0CSID = 2革娄。協(xié)議控制消息收到后立即生效咪橙,它們的時(shí)間戳信息是被忽略的填抬。

1、設(shè)置塊大小(Set Chunk Size准颓,Message Type = 1)

用于設(shè)置Chunk Data的最大長(zhǎng)度 寇甸,Chunk Size 默認(rèn)是 128 字節(jié)(不能小于1字節(jié)署尤,通常應(yīng)該不低于 128 字節(jié))蝶锋。通信過(guò)程中可以通過(guò)發(fā)送該消息來(lái)設(shè)置Chunk Data的大小,客戶端或服務(wù)端均可以修改此值言秸。例如软能,假設(shè)一個(gè)客戶端想要發(fā)送 158 字節(jié)的音頻數(shù)據(jù),而最大塊大小為 128 字節(jié)举畸。在這種情況下查排,客戶端可以向服務(wù)端發(fā)送該消息,通知它最大塊大小被設(shè)置為了 158 字節(jié)抄沮,這樣客戶端只用一個(gè)塊就可以發(fā)送這些音頻數(shù)據(jù)跋核,否則需要要將該消息拆分為Chunk Data分別為128 字節(jié)和 30 字節(jié)的 2 個(gè) Chunk 發(fā)送。以下為 Set Chunk Size 協(xié)議控制消息的Chunk Data

Payload for the 'Set Chunk Size' protocol message

0:該位必須為0合是;
chunk size(31位): 該字段以字節(jié)形式保存新的最大塊大小了罪,該值將用于后續(xù)的所有塊的發(fā)送,知道收到新的通知聪全。該值可取值范圍為1~21474836470x7FFFFFFF)泊藕,由于Chunk size 不能大于 Message 的長(zhǎng)度,所以超過(guò) Message 的長(zhǎng)度16772150xFFFFFF)的值是用不上的。

2娃圆、中止消息(Abort Message玫锋,Message Type = 2)

當(dāng)一個(gè) Message 被劃分為多個(gè) Chunk,接受端只接收到了部分 Chunk 時(shí)讼呢,發(fā)送該控制消息表示發(fā)送端不再傳輸同 Message 的 Chunk 了撩鹿,接受端接收到這個(gè)消息后要丟棄已收到不完整的Chunk。該控制消息的Chunk Data中只有一個(gè)CSID字段(32 字節(jié))悦屏,表示丟棄所有已接收到的塊流 ID 為CSID的 Chunk节沦。

Payload for the 'Abort Message' protocol message

3、確認(rèn)消息(Acknowledgement础爬,Message Type = 3)

當(dāng)客戶端或服務(wù)端收到對(duì)端的消息大小等于窗口大懈帷(Window Size)的消息后要回饋一個(gè) ACK 給發(fā)送端告知對(duì)端可以繼續(xù)發(fā)送數(shù)據(jù)。窗口大小就是指在收到接受端返回的 ACK 前可以發(fā)送的最大字節(jié)數(shù)看蚜,返回的 ACK 中會(huì)帶有從發(fā)送上一個(gè) ACK 后接收到的總字節(jié)數(shù)叫搁。

Payload for the 'Acknowledgement' protocol message

4、窗口大小確認(rèn)(Window Acknowledgement Size供炎,Message Type = 5)

客戶端或服務(wù)端發(fā)送該消息來(lái)告知對(duì)端在兩個(gè) ACK 之間所使用的窗口大小渴逻。發(fā)送端發(fā)送窗口大小的數(shù)據(jù)后等待接收端發(fā)送 ACK。接收端在上一個(gè) ACK 發(fā)送之后音诫,接收到窗口大小的數(shù)據(jù)后必須發(fā)送 ACK惨奕,如果之前沒(méi)有發(fā)送過(guò) ACK 就從會(huì)話開(kāi)始之后算起接收到窗口大小的數(shù)據(jù)后發(fā)送 ACK。

Payload for the 'Window Acknowledgement Size' protocol message

5纽竣、設(shè)置對(duì)等帶寬(Set Peer Bandwidth墓贿,Message Type = 6)

客戶端或服務(wù)端發(fā)送該消息用來(lái)限制對(duì)端的輸出帶寬。對(duì)端收到該消息后通過(guò)將已發(fā)送但未收到的消息大小限制為該消息中的Acknowledgement Window size來(lái)實(shí)現(xiàn)限制發(fā)送帶寬蜓氨。如果該消息中的Acknowledgement Window size與上一次發(fā)送給發(fā)送端的不同的話發(fā)送端要回饋一個(gè) Window Acknowledgement Size 消息。

Payload for the 'Set Peer Bandwidth' protocol message

Limit Type(限制類(lèi)型)有以下選項(xiàng):
0 - Hard:消息接收端應(yīng)該將輸出帶寬限制為該消息中Acknowledgement Window size指定的大卸游啊穴吹;
1 - Soft:消息接收端應(yīng)該將輸出帶寬限制為該消息中Acknowledgement Window size指定的大小或者當(dāng)前窗口大小,限制為二者中的最小值嗜侮。
2 - Dynamic:如果上次的 Set Peer Bandwidth 消息中的Limit Type0 - Hard港令,本次也按0 - Hard處理,否則忽略本消息锈颗。

四顷霹、RTMP 消息類(lèi)型(Types of Messages)

1、命令消息(Command Message击吱,Message Type = 20淋淀,Message Type = 17)

用于客戶端和服務(wù)器間傳遞在對(duì)端執(zhí)行某些操作的命令消息,比如發(fā)送這些消息來(lái)完成連接覆醇、創(chuàng)建流朵纷、發(fā)布炭臭、播放、暫停等操作袍辞,以及通知發(fā)送者命令請(qǐng)求狀態(tài)和結(jié)果等等鞋仍。當(dāng)消息使用AMF0編碼時(shí),Message Type = 20搅吁,使用AMF3編碼時(shí) Message Type = 17威创。

命令消息中包含Command Name(命令名稱(chēng))、transaction ID(命令標(biāo)識(shí))谎懦、command object(相關(guān)參數(shù))那婉。接受端收到命令消息后,會(huì)返回發(fā)送端以下三種消息中的一種:_result響應(yīng)消息表示接受該命令党瓮,對(duì)端可以繼續(xù)往下執(zhí)行流程详炬;_error響應(yīng)消息代表拒絕該命令要執(zhí)行的操作;method name響應(yīng)消息代表要在命令消息的發(fā)送端執(zhí)行的函數(shù)名稱(chēng)寞奸。響應(yīng)消息都要帶有當(dāng)前收到的命令消息中的 transaction ID 來(lái)表示接收端本次回應(yīng)的是哪個(gè)命令消息呛谜。

1.1、連接層命令(NetConnection Commands)

連接層命令用于管理客戶端和服務(wù)端之間的鏈接狀態(tài)枪萄。同時(shí)也提供了異步遠(yuǎn)程方法調(diào)用(RPC)在對(duì)端執(zhí)行某方法隐岛,以下是常見(jiàn)的連接層的命令:

1.1.1、connect

用于客戶端向服務(wù)器發(fā)送鏈接請(qǐng)求瓷翻【郯迹客戶端發(fā)送到服務(wù)端的消息結(jié)構(gòu)如下:
Command Name:命令名稱(chēng),當(dāng)前命令設(shè)置為 "connect"齐帚;
Transaction ID:對(duì)于連接請(qǐng)求該字段恒為 1妒牙;
Command Object:命令參數(shù),用鍵值對(duì)集合表示(可參考官方文檔 7.2.1.1 章節(jié))对妄;
Optional User:用戶自定義的額外信息湘今;

Message flow in the connect command

執(zhí)行 connect 命令時(shí)的消息流順序:
1、客戶端向服務(wù)端發(fā)送 connect 命令消息請(qǐng)求建立連接剪菱;
2摩瞎、服務(wù)端收到 connect 命令消息后向客戶端發(fā)送 Window Acknowledgement Size(窗口大小確認(rèn))消息,并且服務(wù)端連接 connect 命令消息提及的應(yīng)用孝常;
3旗们、服務(wù)端向客戶端發(fā)送 Set Peer Bandwidth(設(shè)置對(duì)等帶寬)消息;
4构灸、客戶端收到 Set Peer Bandwidth 消息后回饋 Window Acknowledgement Size 消息給服務(wù)端上渴;
5、服務(wù)端向客戶端發(fā)送 StreamBegin 消息;
6驰贷、服務(wù)端向客戶端發(fā)送transaction ID = 1的回應(yīng)消息給客戶端盛嘿,服務(wù)端消息的回應(yīng)有兩種:_result表示接受連接,_error表示連接失敗括袒。

1.1.2次兆、call

用于遠(yuǎn)程在對(duì)端執(zhí)行某函數(shù)(即 RPC:遠(yuǎn)程進(jìn)程調(diào)用)。消息的結(jié)構(gòu)如下:
Procedure Name:要調(diào)用的遠(yuǎn)程進(jìn)程名稱(chēng)锹锰;
Transaction ID:如果想要對(duì)端發(fā)送響應(yīng)消息芥炭,需要設(shè)置為非 0 值,否則置為 0恃慧;
Command Object:命令參數(shù)园蝠;
Optional Arguments:用戶自定義參數(shù);

如果消息的Transaction ID不為 0 的話痢士,對(duì)端需要對(duì)該命令做出響應(yīng)彪薛,響應(yīng)的消息結(jié)構(gòu)如下:
Command Name:命令名稱(chēng);
Transaction ID:接收到的命令消息中的Transaction ID怠蹂;
Command Object:命令參數(shù)善延;
Response:調(diào)用方法的響應(yīng);

1.1.3城侧、createStream

客戶端發(fā)給服務(wù)端此命令消息創(chuàng)建傳輸消息的通道易遣,從而可以在這個(gè)流中傳輸音頻、視頻或者元數(shù)據(jù)等嫌佑,傳輸信息單元為 Chunk豆茫。

客戶端發(fā)給服務(wù)端消息的結(jié)構(gòu):
Command Name:命令名稱(chēng),當(dāng)前命令設(shè)置為 "createStream"屋摇;
Transaction ID:消息標(biāo)識(shí)揩魂;
Command Object:命令參數(shù);

服務(wù)端回饋給客戶端消息結(jié)構(gòu):
Command Name:命令名稱(chēng)摊册,當(dāng)前命令設(shè)置為 "createStream"肤京;
Transaction ID:與客戶端發(fā)送到服務(wù)端消息的Transaction ID相同;
Command Object:命令參數(shù)茅特;
Stream ID:返回 Stream ID 或者錯(cuò)誤信息對(duì)象。

1.2棋枕、流連接層命令(NetStream Commands)

NetStream 建立在 NetConnection 之上白修,用于定義傳輸音頻和視頻等信息的通道。在傳輸層協(xié)議之上只能連接一個(gè) NetConnection重斑,但一個(gè) NetConnection 可以建立多個(gè) NetStream 來(lái)建立不同的流通道傳輸數(shù)據(jù)兵睛。服務(wù)端收到命令后會(huì)通過(guò) onStatus 命令來(lái)響應(yīng)客戶端,表示當(dāng)前 NetStream 的狀態(tài)。onStatus 命令消息結(jié)構(gòu)如下:

2祖很、數(shù)據(jù)消息(Data Message笛丙,Message Type = 18,Message Type = 15)

傳遞一些 MetaData(元數(shù)據(jù)假颇,比如主題胚鸯、創(chuàng)建時(shí)間和時(shí)長(zhǎng)等等)或者用戶自定義的一些消息。當(dāng)消息使用AMF0編碼時(shí)笨鸡,Message Type = 18姜钳,使用AMF3編碼時(shí) Message Type = 15。

3形耗、共享消息(Shared Object Message哥桥,Message Type = 19,Message Type = 16)

表示一個(gè) Flash 類(lèi)型的對(duì)象(由鍵值對(duì)的集合組成)激涤,用于多客戶端拟糕,多實(shí)例進(jìn)行同步時(shí)使用。每條消息可包含多個(gè)事件倦踢。當(dāng)消息使用AMF0編碼時(shí)送滞,Message Type = 19,使用AMF3編碼時(shí) Message Type = 16硼一。

4累澡、音頻消息(Audio Message,Message Type = 8)

客戶端或服務(wù)端通過(guò)發(fā)送此消息來(lái)發(fā)送音頻數(shù)據(jù)給對(duì)方般贼。

5愧哟、視頻消息(Video Message,Message Type = 9)

客戶端或服務(wù)端通過(guò)發(fā)送此消息來(lái)發(fā)送視頻數(shù)據(jù)給對(duì)方哼蛆。

6蕊梧、組合消息(Aggregate Message,Message Type = 22)

一個(gè)組合消息消息包含多個(gè)子 RTMP 消息腮介。

The Aggregate Message body format

7肥矢、用戶控制消息(User Control Messages,Message Type = 4)

RTMP 協(xié)議將Message Type4的消息作為了用戶控制消息叠洗,在 Chunk Stream 中發(fā)送時(shí)甘改,Message Stream ID = 0CSID = 2灭抑。接收端收到用戶控制消息后立即生效十艾,用戶控制消息的時(shí)間戳信息是被忽略的。和前面提到的協(xié)議控制消息不同腾节,用戶控制消息是在 RTMP Protocol 層的忘嫉,而不是在 RTMP Chunk Stream Protocol 層的荤牍。

客戶端或服務(wù)端通過(guò)發(fā)送該消息告知對(duì)端用戶控制事件。該消息攜帶事件類(lèi)型和事件數(shù)據(jù)兩部分庆冕。開(kāi)頭的 2 個(gè)字節(jié)用于指定事件類(lèi)型康吵,后面緊跟著是事件數(shù)據(jù)。事件數(shù)據(jù)字段長(zhǎng)度可變访递,使用 RTMP Chunk Stream 傳輸時(shí)晦嵌,最大塊大小要足夠大,以便可以使用一個(gè)單獨(dú)的 Chunk 進(jìn)行傳輸用戶控制消息力九。

Payload for the 'User Control' protocol message

用戶控制消息支持以下事件:
0(流開(kāi)始):服務(wù)端發(fā)送該事件耍铜,用來(lái)通知客戶端一個(gè)流已經(jīng)可以用來(lái)傳輸消息了。默認(rèn)情況下跌前,該事件是在收到客戶端連接指令并成功處理后發(fā)送的第一個(gè)事件棕兼。該事件的Event Data使用 4 個(gè)字節(jié)來(lái)表示可用的message stream id
1(流結(jié)束):服務(wù)端發(fā)送該事件抵乓,用來(lái)通知客戶端其在流中請(qǐng)求的回放數(shù)據(jù)已經(jīng)結(jié)束了伴挚。如果沒(méi)有額外的指令,將不會(huì)再發(fā)送任何數(shù)據(jù)灾炭,而客戶端會(huì)丟棄之后從該流接收到的消息茎芋。該事件的Event Data使用 4 個(gè)字節(jié)來(lái)表示回放完成的message stream id
2(流枯竭):服務(wù)端發(fā)送該事件蜈出,用來(lái)通知客戶端流中沒(méi)有更多數(shù)據(jù)了田弥。如果服務(wù)端在一定時(shí)間后沒(méi)有探測(cè)到更多數(shù)據(jù),它就可以通知所有訂閱該流的客戶端铡原,流已經(jīng)枯竭偷厦。該事件的Event Data使用 4 個(gè)字節(jié)來(lái)表示枯竭的message stream id
3(設(shè)置緩沖區(qū)大醒嗫獭):客戶端發(fā)送該事件只泼,用來(lái)告知服務(wù)端緩沖區(qū)大小(單位毫秒)卵洗。該事件在服務(wù)端開(kāi)始處理流數(shù)據(jù)之前發(fā)送请唱。Event Data中,前 4 個(gè)字節(jié)表示message stream id过蹂,后 4 字節(jié)表示緩沖區(qū)大惺蟆(單位毫秒)。
4(流已錄制):服務(wù)端發(fā)送該事件酷勺,用來(lái)通知客戶端該流是一個(gè)錄制流孽惰。事件數(shù)據(jù)用4個(gè)字節(jié)表示錄制的message stream id
6(ping請(qǐng)求):服務(wù)端發(fā)送該事件鸥印,用來(lái)探測(cè)客戶端是否處于可達(dá)狀態(tài)勋功。Event Data里攜帶的是一個(gè) 4 字節(jié)長(zhǎng)度的時(shí)間戳,表示服務(wù)端分發(fā)該事件時(shí)的服務(wù)器本地時(shí)間库说】裥客戶端收到后用 ping 請(qǐng)求后應(yīng)該回復(fù)服務(wù)端。
7(ping響應(yīng)):客戶端用該事件響應(yīng)服務(wù)端的 ping 請(qǐng)求潜的,Event Data為收到的 ping 請(qǐng)求中攜帶的時(shí)間戳骚揍,長(zhǎng)度 4 字節(jié)。

五啰挪、握手(Handshake)

RTMP 的連接開(kāi)始于握手信不。握手內(nèi)容不同于協(xié)議的其它部分,它包含三個(gè)固定大小的塊亡呵,而不是帶頭信息的變長(zhǎng)塊抽活。

客戶端(發(fā)起連接的端點(diǎn))和服務(wù)器各自發(fā)送相同的三個(gè)塊。為了演示锰什,這三個(gè)塊客戶端發(fā)送的被記做 C0下硕,C1,C2汁胆,服務(wù)發(fā)送的被記做 S0梭姓,S1,S2嫩码。

1誉尖、握手順序

RTMP 協(xié)議本身并沒(méi)有規(guī)定這 6 個(gè)消息的具體傳輸順序,但 RTMP 協(xié)議的實(shí)現(xiàn)者需要保證這幾點(diǎn):

客戶端發(fā)送 C0 和 C1 塊開(kāi)始握手铸题。
客戶端必須等接收到 S1 后才能發(fā)送 C2铡恕。
客戶端必須等接收到 S2 后才能發(fā)送其它數(shù)據(jù)。

服務(wù)器必須等接收到 C0 才能發(fā)送 S0 和 S1回挽,也可以等接到 C1 一起之后没咙。
服務(wù)器必須等到 C1 才能發(fā)送 S2。
服務(wù)器必須等到 C2 才能發(fā)送其它數(shù)據(jù)千劈。

2祭刚、C0 和 S0 格式

C0 和 S0 包只有八個(gè)位,可以看成一個(gè) 8 位的整數(shù)墙牌。

C0 and S0 bits

以下是CO和S0包的字段解釋?zhuān)?/p>

版本號(hào)(8 位):在 C0 包中涡驮,該字段表示客戶端請(qǐng)求的 RTMP 版本。在 S0 中喜滨,該字段表示服務(wù)器選擇的 RTMP 版本捉捅。本規(guī)范所定義的版本是 3∷浞纾可選值中棒口,0-2 是早期版本所用的寄月,已被丟棄;4-31 保留在未來(lái)使用无牵;32-255 不允許使用(為了區(qū)分其他以某一可見(jiàn)字符開(kāi)始的文本協(xié)議)漾肮。如果服務(wù)器不能識(shí)別客戶端請(qǐng)求的版本, 應(yīng)該返回 3茎毁,客戶端可能選擇降級(jí)到版本3克懊,也可能放棄握手。

3七蜘、C1 和 S1 格式

C1 and S1 bits

C1 和 S1 包的長(zhǎng)度固定為1536字節(jié)谭溉, 包含以下字段:

時(shí)間戳(4 字節(jié)):該字段承載一個(gè)時(shí)間戳,該時(shí)間戳應(yīng)該作為發(fā)送端點(diǎn)所有后續(xù)塊的時(shí)間戳起始時(shí)間橡卤“缒睿可以是 0,也可以是其他的任意值蒜魄。為了同步多個(gè)塊流扔亥,端點(diǎn)可能會(huì)發(fā)送其他塊流時(shí)間戳的當(dāng)前值。
零值(4 字節(jié)):該字段所有值都必須為 0谈为。
隨機(jī)數(shù)據(jù)(1528 字節(jié)):該字段可以是任意值旅挤。因?yàn)槊總€(gè)端點(diǎn)都需要區(qū)分自己和其他端點(diǎn)初始化的握手響應(yīng),所以此數(shù)據(jù)應(yīng)該有充分的隨機(jī)性伞鲫,但是沒(méi)必要使用加密安全的隨機(jī)值或動(dòng)態(tài)值粘茄。

4、C2 和 S2 格式

C2 and S2 bits

C2 和 S2 包的長(zhǎng)度固定為 1536 字節(jié)秕脓,基本上分別是 S1 和 C1 的回復(fù)柒瓣, 包含以下字段:

時(shí)間(4字節(jié)): 這個(gè)字段必須包含了一個(gè)時(shí)間戳,它是由對(duì)端發(fā)送過(guò)來(lái)吠架。對(duì)于 C2 來(lái)說(shuō)是 S1芙贫,對(duì)于 S2 來(lái)說(shuō)是 C1;
時(shí)間2(4字節(jié)):這個(gè)字段必須包含前一個(gè)對(duì)端發(fā)送過(guò)來(lái)的并被讀取的包(S1 或 C1)傍药;
隨機(jī)數(shù)(1528字節(jié)):這個(gè)字段必須包含對(duì)端發(fā)送過(guò)來(lái)的隨機(jī)數(shù)對(duì) S1 來(lái)說(shuō)是 C2磺平,對(duì)于 S2 來(lái)說(shuō)是 C1。兩端都可以時(shí)間字段和時(shí)間 2 字段結(jié)合當(dāng)前的時(shí)間來(lái)快速寬帶和(或)連接的延遲拐辽,但這個(gè)方法不太可能很有用處拣挪。

5、握手流程示意圖

Handshake

下面是握手圖示中提到的各個(gè)階段具體內(nèi)容:

未初始化(Uninitialized):協(xié)議的版本發(fā)送出去的這個(gè)階段俱诸〔と埃客戶端與服務(wù)器都是未初始化態(tài)≌龃睿客戶端在 C0 中發(fā)送協(xié)議版本赶诊。如果服務(wù)器支持這個(gè)版本笼平,它將回應(yīng) S0 和 S1。如果不支持甫何,服務(wù)器將會(huì)采取適當(dāng)措施的回應(yīng)出吹。在 RTMP 中,這個(gè)措施是終止連接辙喂;
版本已發(fā)送(Version Sent):客戶端和服務(wù)器在未初始化態(tài)后是版本已發(fā)送態(tài)○椋客戶端等待 S1巍耗,服務(wù)器在等待 C1。在接收到響應(yīng)包后渐排,客戶端發(fā)送 C2炬太,服務(wù)器發(fā)送 S2。狀態(tài)就變成了確認(rèn)已發(fā)送驯耻;
確認(rèn)已發(fā)送(Ack Send):客戶端和服務(wù)器分別在等 S2 和 C2亲族;
握手完成(Handshake Done):客戶端與服務(wù)器可以交換消息了。

參考:
RTMP Specification 1.0

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末可缚,一起剝皮案震驚了整個(gè)濱河市霎迫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帘靡,老刑警劉巖知给,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異描姚,居然都是意外死亡涩赢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)轩勘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)筒扒,“玉大人,你說(shuō)我怎么就攤上這事绊寻』ǘ眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵榛斯,是天一觀的道長(zhǎng)观游。 經(jīng)常有香客問(wèn)我,道長(zhǎng)驮俗,這世上最難降的妖魔是什么懂缕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮王凑,結(jié)果婚禮上搪柑,老公的妹妹穿的比我還像新娘聋丝。我一直安慰自己,他們只是感情好工碾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布弱睦。 她就那樣靜靜地躺著,像睡著了一般渊额。 火紅的嫁衣襯著肌膚如雪况木。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天旬迹,我揣著相機(jī)與錄音火惊,去河邊找鬼。 笑死奔垦,一個(gè)胖子當(dāng)著我的面吹牛屹耐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椿猎,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惶岭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了犯眠?” 一聲冷哼從身側(cè)響起按灶,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎阔逼,沒(méi)想到半個(gè)月后兆衅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗜浮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年羡亩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片危融。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畏铆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吉殃,到底是詐尸還是另有隱情辞居,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布蛋勺,位于F島的核電站瓦灶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抱完。R本人自食惡果不足惜贼陶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碉怔,春花似錦烘贴、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至芹啥,卻和暖如春锻离,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叁征。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工纳账, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捺疼。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像永罚,于是被迫代替她去往敵國(guó)和親啤呼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容