1.RTMP描述
RTMP協(xié)議是Real Time Message Protocol(實(shí)時信息傳輸協(xié)議)的縮寫,它是由Adobe公司提出的?種應(yīng)?層的協(xié)議,?來解決多媒體數(shù)據(jù)傳輸流的多路復(fù)?(Multiplexing)和分包(packetizing)的問題。隨著VR技術(shù)的發(fā)展,視頻直播等領(lǐng)域逐漸活躍起來,RTMP作為業(yè)內(nèi)?泛使?的協(xié)議也重新被相關(guān)開發(fā)者重視起來。
RTMP協(xié)議是應(yīng)?層協(xié)議,是要靠底層可靠的傳輸層協(xié)議(通常是TCP)來保證信息傳輸?shù)目煽啃缘摹T诨趥鬏攲訁f(xié)議的鏈接建?完成后,RTMP協(xié)議也要客戶端和服務(wù)器通過“握?”來建?基于傳輸層鏈接之上的RTMP Connection鏈接,在Connection鏈接上會傳輸?些控制信息,如SetChunkSize,SetACKwindowsize。其中CreateStream命令會創(chuàng)建?個Stream鏈接,?于傳輸具體的?視頻數(shù)據(jù)和控制這些信息傳輸?shù)?strong>命令信息。RTMP協(xié)議傳輸時會對數(shù)據(jù)做??的格式化,這種格式的消息我們稱之為RTMP Message,?實(shí)際傳輸?shù)臅r候?yàn)榱烁玫?strong>實(shí)現(xiàn)多路復(fù)?、分包和信息的公平性,發(fā)送端會把Message劃分為帶有Message ID的Chunk,每個Chunk可能是?個單獨(dú)的Message,也可能是Message的?部分,在接收端會根據(jù)chunk中包含的data的?度,message id和message的?度把chunk還原成完整的Message,從?實(shí)現(xiàn)信息的收發(fā)。
2.RTMP握手
RTMP握手也分為簡單握手和復(fù)雜握手,可以參考前面文章,有詳細(xì)說明。
要建個有效的RTMP Connection鏈接,?先要“握?”:客戶端要向服務(wù)器發(fā)送C0,C1,C2(按序)三個chunk,服務(wù)器向客戶端發(fā)送S0,S1,S2(按序)三個chunk,然后才能進(jìn)?有效的信息傳輸。RTMP協(xié)議本身并沒有規(guī)定這6個Message的具體傳輸順序,但RTMP協(xié)議的實(shí)現(xiàn)者需要保證這?點(diǎn):
(1)客戶端要等收到S1之后才能發(fā)送C2。
(2)客戶端要等收到S2之后才能發(fā)送其他信息(控制信息和真實(shí)?視頻等數(shù)據(jù))。
(3)服務(wù)端要等到收到C0之后發(fā)送S1,涉及到s1對C0的數(shù)據(jù)拷貝。
(4)服務(wù)端必須等到收到C1之后才能發(fā)送S2,涉及到S2對C1的數(shù)據(jù)拷貝。
(5)服務(wù)端必須等到收到C2之后才能發(fā)送其他信息(控制信息和真實(shí)?視頻等數(shù)據(jù))。
如果每次發(fā)送?個握?chunk的話握?順序會是這樣(簡單握手),如下圖:

理論上來講只要滿?以上條件,如何安排6個Message的順序都是可以的,但實(shí)際實(shí)現(xiàn)中為了在保證握?的身份驗(yàn)證功能的基礎(chǔ)上盡量減少通信的次數(shù),?般的發(fā)送順序是這樣的,這?點(diǎn)可以通過wireshark抓ffmpeg推流包進(jìn)?驗(yàn)證,如下圖:

3. RTMP Chunk Stream
Chunk Stream是對傳輸RTMP Chunk的流的邏輯上的抽象,客戶端和服務(wù)器之間有關(guān)RTMP的信息都在這個流上通信。這個流上的操作也是我們關(guān)注RTMP協(xié)議的重點(diǎn)。
3.1 Message(消息)
這?的Message(flv的tag需要封裝成Message)是指滿?該協(xié)議格式的、可以切分成Chunk發(fā)送的消息,消息包含的字段如下:
(1)Timestamp(時間戳):消息的時間戳(但不?定是當(dāng)前時間,后?會介紹),4個字節(jié)表示。
(2)Length(?度):是指Message Payload(消息負(fù)載)即?視頻等信息的數(shù)據(jù)的?度,3個字節(jié)表示。
(3)TypeId(類型Id):消息的類型Id,1個字節(jié)。
(4)Message Stream ID(消息的流ID):每個消息的唯?標(biāo)識,劃分成Chunk和還原Chunk為Message的時候都是根據(jù)這個ID來辨識是否是同?個消息的Chunk的,4個字節(jié),并且以?端格式存儲。
3.2 Chunking(Message分塊)
RTMP在收發(fā)數(shù)據(jù)的時候并不是以Message為單位的,?是把Message拆分成Chunk發(fā)送,?且必須在?個Chunk發(fā)送完成之后才能開始發(fā)送下?個Chunk(一般不會交替發(fā)送)。每個Chunk中帶有Message ID代表屬于哪個Message,接收端也會按照這個id來將chunk組裝成Message。
為什么RTMP要將Message拆分成不同的Chunk呢?
這個在前面的文章也分析過,可以參考前面文章流媒體推拉流實(shí)戰(zhàn)之RTMP協(xié)議分析(BAT面試官推薦) ,有以下2個原因:
(1)分包能夠減小延時和阻塞,更能適應(yīng)復(fù)雜網(wǎng)絡(luò)環(huán)境變化。
(2) 通過拆分,數(shù)據(jù)量較?的Message可以被拆分成較?的“Message”,這樣就可以避免優(yōu)先級低(數(shù)據(jù)量大)的消息持續(xù)發(fā)送阻塞優(yōu)先級?(數(shù)據(jù)量低)的數(shù)據(jù),?如在視頻的傳輸過程中,會包括視頻幀,?頻幀和RTMP控制信息,如果持續(xù)發(fā)送?頻數(shù)據(jù)或者控制數(shù)據(jù)的話可能就會造成視頻幀的阻塞,然后就會造成看視頻時最煩?的卡頓現(xiàn)象。同時對于數(shù)據(jù)量較?的Message,可以通過對Chunk Header的字段來壓縮信息,從?減少信息的傳輸量。(具體的壓縮?式會在后?介紹)
Chunk包大小如何設(shè)置?
設(shè)置Chunk包大小主要考慮2點(diǎn),一個是CPU占有率,一個是網(wǎng)絡(luò)發(fā)送時間和是否阻塞。這兩點(diǎn)是矛盾關(guān)系,需要根據(jù)實(shí)際情況,折中考慮。Chunk的默認(rèn)??是128字節(jié),在傳輸過程中,通過?個叫做Set Chunk Size的控制信息(?spec 5.4.1 )可以設(shè)置Chunk數(shù)據(jù)量的最?值,在發(fā)送端和接受端會各?維護(hù)?個Chunk Size(srs流媒體服務(wù)器默認(rèn)是60000),可以分別設(shè)置這個值來改變??這??發(fā)送的Chunk的最???。
如果考慮CPU占有率減少多一點(diǎn)。那就選擇大一點(diǎn)的Chunk,??點(diǎn)的Chunk減少了計算每個chunk的發(fā)送時間,從?減少了CPU的占?率,但是它會占?更多的時間在發(fā)送上,尤其是在低帶寬的?絡(luò)情況下,很可能會阻塞后?更重要信息的傳輸。
如果考慮網(wǎng)絡(luò)發(fā)送時間和是否阻塞多一點(diǎn)。那就選擇小一點(diǎn)的Chunk,??點(diǎn)的Chunk可以減少這種阻塞問題,但?的Chunk會引?過多額外的信息(Chunk中的Header),少量多次的傳輸也可能會造成發(fā)送的間斷導(dǎo)致不能充分利??帶寬的優(yōu)勢,因此CPU使用率會更高。
總的來說,Chunk小一點(diǎn),不適合在高比特率的流中傳輸。在實(shí)際發(fā)送時應(yīng)對要發(fā)送的數(shù)據(jù)?不同的Chunk Size去嘗試,通過抓包分析等?段得出合適的Chunk??,并且在傳輸過程中可以根據(jù)當(dāng)前的帶寬信息和實(shí)際信息的??動態(tài)調(diào)整Chunk的??,從?盡量提?CPU的利?率并減少信息的阻塞機(jī)率。折中考慮,弄清這2點(diǎn),也是調(diào)優(yōu)的關(guān)鍵。
3.3 Chunk Format(塊格式)
這個格式分析,在前面文章分析過,可以參看前面文章。流媒體推拉流實(shí)戰(zhàn)之RTMP協(xié)議分析(BAT面試官推薦)

這里有個問題就是,?個流當(dāng)中可以交錯傳輸多種消息類型的Chunk,那么多個Chunk怎么標(biāo)記同屬于同?類Message的呢?
回答:通過Chunk Stream ID區(qū)分的,同?個Chunk Stream ID必然屬于同?個Message。
還有個問題就是,Basic Header??的chunk stream ID代表了通道,不同的Message 是否有不同的的chunk Stream ID?
回答:不同的Message的chunk Stream ID是不一樣。RTMP流中視頻和?頻擁有單獨(dú)的Chunk Stream ID,?如?頻的cs id=20,視頻的cs id=21。接收端接收到Chunk之后,根據(jù)cs id分別將?頻和視頻“拼成成Message消息”。
注意:每?種消息類型的起始chunk 的類型必須是 Type_0 類型的,表明我是?個新的消息的起始。
3.3.1 Basic Header(基本的頭信息)
包含了chunk stream ID(流通道Id)和chunk type(chunk的類型),chunk stream id?般被簡寫為CSID,?來唯?標(biāo)識?個特定的流通道,chunk type決定了后?Message Header的格式。Basic Header的?度可能是1,2,或3個字節(jié),其中chunk type的?度是固定的(占2位,注意單位是位,bit),Basic Header是變?,其?度取決于CSID的??,在?夠存儲這兩個字段的前提下最好?盡量少的字節(jié)從?減少由于引?Header增加的數(shù)據(jù)量。
RTMP協(xié)議最多?持65597個?戶?定義chunk stream ID,范圍為[3,65599] ,ID 0, 1, 2被協(xié)議規(guī)范直接使?,其中ID值為0, 1分表表示了Basic Header占?2個字節(jié)和3個字節(jié):
(1)當(dāng)Basic Header為1個字節(jié)時,CSID占6位,6位最多可以表示64個數(shù),因此這種情況下CSID在 [0,63] 之間,其中?戶可?定義的范圍為 [3,63] ,實(shí)際是可以?2開始?。1個字節(jié)的Basic Header如下圖:

(2)ID值0:代表Basic Header占?2個字節(jié),CSID在 [64,319] 之間。
當(dāng)Basic Header為2個字節(jié)時,結(jié)構(gòu)如下圖,CSID占只占8位,第?個字節(jié)除chunk type占?的bit都置為0,第?個字節(jié)?來表示CSID-64,8位可以表示 [0, 255] 共256個數(shù),ID的計算?法為(第?個字節(jié)+64),范圍為 [64,319]。

(3)ID值1:代表Basic Header占?3個字節(jié),CSID在 [64,65599] 之間。
當(dāng)Basic Header為3個字節(jié)時,結(jié)構(gòu)如下圖,以在此字段?3字節(jié)版本編碼。ID的計算?法為(第三字節(jié)*256+第?字節(jié)+64)(Basic Header是采??端存儲的?式),范圍為 [64,65599]。可以看到2個字節(jié)和3個字節(jié)的Basic Header所能表示的CSID是有交集的 [64,319],但實(shí)際實(shí)現(xiàn)時還是應(yīng)該秉著最少字節(jié)的原則使?2個字節(jié)的表示?式來表示 [64,319] 的CSID。

(4)ID值2:代表該chunk是控制信息和?些命令信息,后?會有詳細(xì)的介紹。
3.3.2 Message Header(消息的頭信息)
包含了要發(fā)送的實(shí)際信息(可能是完整的,也可能是?部分)的描述信息。Message Header的格式和?度取決于Basic Header的chunk type,共有4種不同的格式,這在前面的文章也有詳細(xì)的分析過,流媒體推拉流實(shí)戰(zhàn)之RTMP協(xié)議分析(BAT面試官推薦)。由上?所提到的Basic Header中的fmt字段控制。其中第?種格式可以表示其他三種表示的所有數(shù)據(jù),但由于其他三種格式是基于對之前chunk的差量化的表示,因此可以更簡潔地表示相同的數(shù)據(jù),實(shí)際使?的時候還是應(yīng)該采?盡量少的字節(jié)表示相同意義的數(shù)據(jù)。以下按照字節(jié)數(shù)從多到少的順序分別介紹這4種格式的Message Header。
Type=0: 占?11個字節(jié)
type=0時Message Header占?11個字節(jié),其他三種能表示的數(shù)據(jù)它都能表示,但在chunk stream的開始的第?個chunk和頭信息中的時間戳后退(即值與上?個chunk相?減?,通常在回退播放的時候會出現(xiàn)這種情況)的時候必須采?這種格式。

(1)timestamp(時間戳):占?3個字節(jié),因此它最多能表示到16777215=0xFFFFFF=2^24-1, 當(dāng)它的值超過這個最?值時,這三個字節(jié)都置為1,這樣實(shí)際的timestamp會轉(zhuǎn)存到Extended Timestamp字段中,接收端在判斷timestamp字段24個位都為1時就會去Extended timestamp中解析實(shí)際的時間戳。
(2)字段message length和message length(cont)(消息數(shù)據(jù)的?度),占?3個字節(jié),表示實(shí)際發(fā)送的消息的數(shù)據(jù)如?頻幀、視頻幀等數(shù)據(jù)的?度,單位是字節(jié)。注意這?是Message的?度,也就是chunk屬于的Message的總數(shù)據(jù)?度,?不是chunk本身Data的數(shù)據(jù)的?度。
(3)message type id(消息的類型id):占?1個字節(jié),表示實(shí)際發(fā)送的數(shù)據(jù)的類型,如8代表?頻數(shù)據(jù)、9代表視頻數(shù)據(jù)。
(4)msg stream id(消息的流id):占?4個字節(jié),表示該chunk所在的流的ID,和Basic Header的CSID?樣,它采??端存儲的?式。
Type = 1:占?7個字節(jié)
type=1時Message Header占?7個字節(jié),省去了表示msg stream id的4個字節(jié),表示此chunk和上?次發(fā)的chunk所在的流相同,如果在發(fā)送端只和對端有?個流鏈接的時候可以盡量去采取這種格式。

(1)timestamp delta:占?3個字節(jié),注意這?和type=0時不同,存儲的是和上?個chunk的時間差。類似上?提到的timestamp,當(dāng)它的值超過3個字節(jié)所能表示的最?值時,三個字節(jié)都置為1,實(shí)際的時間戳差值就會轉(zhuǎn)存到Extended Timestamp字段中,接受端在判斷timestamp delta字段24個位都為1時就會去Extended timestamp中解析時機(jī)的與上次時間戳的差值。
Type = 2:占?3個字節(jié)
type=2時Message Header占?3個字節(jié),相對于type=1格式?省去了表示消息?度的3個字節(jié)和表示消息類型的1個字節(jié),表示此chunk和上?次發(fā)送的chunk所在的流、消息的?度和消息的類型都相同。余下的這三個字節(jié)表示timestamp delta,使?同type=1。

Type = 3:占?0字節(jié)
0字節(jié)!!!好吧,它表示這個chunk的Message Header和上?個是完全相同的,?然就不?再傳輸?遍了。當(dāng)它跟在Type=0的chunk后?時,表示和前?個chunk的時間戳都是相同。什么時候連時間戳都相同呢?就是?個Message拆分成了多個chunk,這個chunk和上?個chunk同屬于?個Message。?當(dāng)它跟在Type=1或者Type=2的chunk后?時,表示和前?個chunk的時間戳的差是相同的。?如第?個chunk的Type=0,timestamp=100,第?個chunk的Type=2,timestamp delta=20,表示時間戳為100+20=120,第三個chunk的Type=3,表示timestamp delta=20,時間戳為120+20=140
4種type對?
4種類型對比,在前面文章也有講解,也可以參考前面文章。流媒體推拉流實(shí)戰(zhàn)之RTMP協(xié)議分析(BAT面試官推薦)




3.3.3 Extended Timestamp(擴(kuò)展時間戳)
上?我們提到在chunk中會有時間戳timestamp和時間戳差timestamp delta,并且它們不會同時存在,只有這兩者之??于3個字節(jié)能表示的最?數(shù)值0xFFFFFF=16777215時,才會?這個字段來表示真正的時間戳,否則這個字段為0。擴(kuò)展時間戳占4個字節(jié),能表示的最?數(shù)值就是0xFFFFFFFF=4294967295。當(dāng)擴(kuò)展時間戳啟?時,timestamp字段或者timestamp delta要全置為0xFFFFFF,表示應(yīng)該去擴(kuò)展時間戳字段來提取真正的時間戳或者時間戳差。
注意:擴(kuò)展時間戳存儲的是完整值,?不是減去時間戳或者時間戳差的值。
3.3.4 Chunk Data(塊數(shù)據(jù))
?戶層?上真正想要發(fā)送的與協(xié)議?關(guān)的數(shù)據(jù),?度在(0,chunkSize]之間。
3.3.5 chunk表示例1
這個例?顯示了?個簡單的?頻信息流。這個例?演示了信息的冗余。

看懂下面這段分析,就可以了解到,Message是怎樣做到變長。
(1)第?個Message的chunk的Chunk Type為0,因?yàn)樗鼪]有前?可參考的chunk,timestamp為1000,表示時間戳。type為0的header占?11個字節(jié),假定chunkstreamId為3<127,因此BasicHeader占?1個字節(jié),再加上Data的32個字節(jié),因此第?個chunk共44=11+1+32個字節(jié)。
(2)第?個chunk和第?個chunk的CSID,TypeId,Data的?度都相同,因此采?Chunk Type=2,timestamp delta=1020-1000=20,因此第?個chunk占?36=3+1+32個字節(jié)。
(3)第三個chunk和第?個chunk的CSID,TypeId,Data的?度和時間戳差都相同,因此采?Chunk Type=3省去全部Message Header的信息,占?33=1+32個字節(jié)。
(4)第四個chunk和第三個chunk情況相同,也占?33=1+32個字節(jié)。
最后實(shí)際發(fā)送的chunk如下:

3.3.6 Chunk數(shù)據(jù)量過大
如果一個Message消息因?yàn)樘?,以?于?法適??個128字節(jié)的chunk,從?被分解成多個chunk。
(1)注意到Data的Length=307>128,因此這個Message要切分成?個chunk發(fā)送,第?個chunk的Type=0,Timestamp=1000,承擔(dān)128個字節(jié)的Data,因此共占?140=11+1+128個字節(jié)。
(2)第?個chunk也要發(fā)送128個字節(jié),其他字段也同第?個chunk,因此采?Chunk Type=3,此時時間戳也為1000,共占?129=1+128個字節(jié)
(3)第三個chunk要發(fā)送的Data的?度為307-128-128=51個字節(jié),還是采?Type=3,共占?1+51=52個字節(jié)。

最后實(shí)際發(fā)送的chunk如下:

從兩個例?中注意到,Type = 3的chunk可以?在兩種不同的?式中。第?種是指定消息的繼續(xù)。第?種是指定?個新的消息的開始,它的頭可以來?于現(xiàn)有的狀態(tài)數(shù)據(jù)。
3.4 協(xié)議控制消息(Protocol Control Message)
在RTMP的chunk流會??些特殊的值來代表協(xié)議的控制消息,它們的Message Stream ID必須為0(代表控制流信息),CSID必須為2,Message Type ID可以為1,2,3,5,6,具體代表的消息會在下?依次說明。控制消息的接收端會忽略掉chunk中的時間戳,收到后?即?效。
Set Message Type ID=1
設(shè)置chunk中Data字段所能承載的最?字節(jié)數(shù),默認(rèn)為128Bytes,通信過程中可以通過發(fā)送該消息來設(shè)置chunk Size的??(不得?于128Bytes),?且通信雙?會各?維護(hù)?個chunkSize,兩端的chunkSize是獨(dú)?。
?如當(dāng)A想向B發(fā)送?個200Bytes的Message,但默認(rèn)的chunkSize是128Bytes,因此就要將該消息拆分為Data分別為128Bytes和72Bytes的兩個chunk發(fā)送。如果此時先發(fā)送?個設(shè)置chunkSize為256Bytes的消息,再發(fā)送Data為200Bytes的chunk,本地不再劃分Message,B接收到Set Chunk Size的協(xié)議控制消息時會調(diào)整的接受的chunk的Data的??,也不?再將兩個chunk組成為?個Message,所以根據(jù)實(shí)際的情況,設(shè)置客戶端和服務(wù)端的Chunk Size值。
在實(shí)際寫代碼的時候?般會把chunk size設(shè)置的很?,有的會設(shè)置為4096,F(xiàn)FMPEG推流的時候設(shè)置的是 60*1000,這樣設(shè)置的好處是避免了頻繁的拆包組包,占?過多的CPU。網(wǎng)絡(luò)不佳時,但發(fā)送可能會耗時。
以下為代表Set Chunk Size消息的chunk的Data:
其中第?位必須為0,chunk Size占31個位,最?可代表2147483647=0x7FFFFFFF=2^31-1,但實(shí)際上所有?于16777215=0xFFFFFF的值都?不上,因?yàn)閏hunk size不能?于Message的?度,表示Message的?度字段是?3個字節(jié)表示,最?只能為0xFFFFFF。

Abort Message (ID=2)
Abort Message(Message Type ID=2):當(dāng)?個Message被切分為多個chunk,接受端只接收到了部分chunk時,發(fā)送該控制消息表示發(fā)送端不再傳輸同Message的chunk,接受端接收到這個消息后要丟棄這些不完整的chunk。Data數(shù)據(jù)中只需要?個CSID,表示丟棄該CSID的所有已接收到的chunk。

Acknowledgement (ID=3)和Window Acknowledgement Size (ID=5)
Window Acknowledgement Size?于設(shè)置窗?確認(rèn)??,Acknowledgement是窗?確認(rèn)消息。會話開始時,雙?都要先對端發(fā)送Window Acknowledgement Size,?于指明期望獲得確認(rèn)的??。當(dāng)?端收到內(nèi)容??超過Window Acknowledgement Size,就要向?qū)?發(fā)送Acknowledgement。詳細(xì)流程如下:
(1)會話開始計算收到byte個數(shù)的時間點(diǎn)是收到Window Acknowledgement Size消息開始。
(2)byte size不包括tcp包頭,應(yīng)該是chunk的??,即從tcp 的recv函數(shù)中獲得的內(nèi)容??。
(3)雙?都要向?qū)?發(fā)送Window Acknowledgement Size和Acknowledgement。
(4)發(fā)送端發(fā)送完Window Acknowledgement Size消息后,沒有收到Acknowledgement是不再發(fā)送進(jìn)?步的消息——這樣會容易引起錯誤,導(dǎo)致再也發(fā)送不出消息了。

對于拉流端,?般在收到av_createStream后,接著play,然后發(fā)送Acknowledgement 以讓服務(wù)器繼續(xù)發(fā)送數(shù)據(jù)。用于相互確認(rèn)發(fā)送窗口大小。
Set Peer Bandwidth (ID=6)
Set Peer Bandwidth(Message Type ID=6):限制對端的輸出帶寬。接受端接收到該消息后會通過設(shè)置消息中的Window ACK Size來限制已發(fā)送但未接受到反饋的消息的??來限制發(fā)送端的發(fā)送帶寬。如果消息中的Window ACK Size與上?次發(fā)送給發(fā)送端的size不同的,要回饋?個WindowAcknowledgement Size的控制消息。同樣,也是要相互確定大小。

詳細(xì)流程如下:
(1)Hard(Limit Type=0):接收端應(yīng)該將Window Ack Size設(shè)置為消息中的值。
(2)Soft(Limit Type=1):接收端可以將Window Ack Size設(shè)為消息中的值,也可以保存原來的值(前提是原來的Size?與該控制消息中的Window Ack Size)
(3)Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit Type為0,本次也按Hard處理,否則忽略本消息,不去設(shè)置Window Ack Size。
4. 不同類型的RTMP Message
(1)Command Message(命令消息,Message Type ID=17或20):表示在客戶端和服務(wù)器間傳遞,在對端執(zhí)?某些操作的命令消息,如connect表示連接對端,對端如果同意連接的話會記錄發(fā)送端信息并返回連接成功消息,publish表示開始向?qū)?推流,接收端接到命令后,準(zhǔn)備好接收對端發(fā)送的流信息,后?會對?較常?的Command Message具體介紹。當(dāng)信息使?AMF0編碼時,Message Type ID=20,AMF3編碼時Message Type ID=17。
(2)Data Message(數(shù)據(jù)消息,Message Type ID=15或18):傳遞?些元數(shù)據(jù)(MetaData,?如視頻名,分辨率等等)或者?戶?定義的?些消息。當(dāng)信息使?AMF0編碼時,Message Type ID=18,AMF3編碼時Message Type ID=15
(3)Shared Object Message(共享消息,Message Type ID=16或19):表示?個Flash類型的對象,由鍵值對的集合組成,?于多客戶端,多實(shí)例時使?。當(dāng)信息使?AMF0編碼時,Message Type ID=19,AMF3編碼時Message Type ID=16。
(4)Audio Message(?頻信息,Message Type ID=8):?頻數(shù)據(jù)。
(5)Video Message(視頻信息,Message Type ID=9):視頻數(shù)據(jù)。
(6)Aggregate Message (聚集信息,Message Type ID=22):多個RTMP?消息的集合。
(7)User Control Message Events(?戶控制消息,Message Type ID=4):告知對?執(zhí)?該信息中包含的?戶控制事件,?如Stream Begin事件告知對?流信息開始傳輸。和前?提到的協(xié)議控制信息(Protocol Control Message)不同,這是在RTMP協(xié)議層(更上層),?不是在RTMP chunk流協(xié)議層的,這個很容易弄混。該信息在chunk流中發(fā)送時,Message Stream ID=0,Chunk Stream Id=2,Message Type Id=4。
以下詳細(xì)介紹這幾種情況。
4.1 Command Message(命令消息,Message Type ID=17 或20)
實(shí)際使?時只是使?了ID=20,發(fā)送端發(fā)送時會帶有命令的名字,如connect,TransactionID表示此次命令的標(biāo)識,Command Object表示相關(guān)參數(shù)。接受端收到命令后,會返回以下三種消息中的?種:
(1)_result消息表示接受該命令,對端可以繼續(xù)往下執(zhí)?流程。
(2)_error消息代表拒絕該命令要執(zhí)?的操作。
(3)method name消息代表要在之前命令的發(fā)送端執(zhí)?的函數(shù)名稱。
注意:這三種回應(yīng)的消息都要帶有收到的命令消息中的Transaction Id,表示本次的回應(yīng)作?于哪個命令。可以認(rèn)為發(fā)送命令消息的對象有兩種,?種是NetConnection,表示雙端的上層連接,?種是NetStream,表示流信息的傳輸通道,控制流信息的狀態(tài),如Play播放流,Pause暫停。
4.1.1 NetConnection Commands(連接層的命令)
?來管理雙端之間的連接狀態(tài),同時也提供了異步遠(yuǎn)程?法調(diào)?(RPC)在對端執(zhí)?某?法,以下是常?的連接層的命令:
connect:?于客戶端向服務(wù)器發(fā)送連接請求,握?之后先發(fā)送?個connect 命令消息,這些信息是以AMF格式發(fā)送的,消息的結(jié)構(gòu)如下:

第三個字段中的Command Object中會涉及到很多鍵值對,使?時可以參考協(xié)議的官??檔。
消息的回應(yīng)有兩種,_result表示接受連接,_error表示連接失敗。
以下是連接命令對象中使?的名稱-值對的描述:

Call:?于在對端執(zhí)?某函數(shù),即常說的RPC:遠(yuǎn)程進(jìn)程調(diào)?,消息的結(jié)構(gòu)如下:

如果消息中的TransactionID不為0的話,對端需要對該命令做出響應(yīng),響應(yīng)的消息結(jié)構(gòu)如下:

Create Stream:創(chuàng)建傳遞具體信息的通道,從?可以在這個流中傳遞具體信息,傳輸信息單元為Chunk。當(dāng)發(fā)送完createStream消息之后,解析服務(wù)器返回的消息會得到?個stream ID, 這個ID也就是以后和服務(wù)器通信的 message stream ID, ?般返回的是1,不固定。

4.1.2 NetStream Commands(流連接上的命令)
Netstream建?在NetConnection之上,通過NetConnection的createStream命令創(chuàng)建,?于傳輸具體的?頻、視頻等信息。在傳輸層協(xié)議之上只能連接?個NetConnection,但?個NetConnection可以建?多個NetStream來建?不同的流通道傳輸數(shù)據(jù)(一定要記住,這在wireshark上可以得到)。
以下會列出?些常?的NetStream Commands,服務(wù)端收到命令后會通過onStatus的命令來響應(yīng)客戶端,表示當(dāng)前NetStream的狀態(tài)。
onStatus命令的消息結(jié)構(gòu)如下:

play(播放):由客戶端向服務(wù)器發(fā)起請求從服務(wù)器端接受數(shù)據(jù)(如果傳輸?shù)男畔⑹且曨l的話就是請求開始播流),可以多次調(diào)?,這樣本地就會形成?組數(shù)據(jù)流的接收者。注意其中有?個reset字段,表示是覆蓋之前的播流(設(shè)為true)還是重新開始?路播放(設(shè)為false)。
play命令的結(jié)構(gòu)如下:

play2(播放):和上?的play命令不同的是,play2命令可以將當(dāng)前正在播放的流切換到同樣數(shù)據(jù)但不同?特率的流上,服務(wù)器端會維護(hù)多種?特率的?件來供客戶端使?play2命令來切換。

deleteStream(刪除流):?于客戶端告知服務(wù)器端本地的某個流對象已被刪除,不需要再傳輸此路流。

receiveAudio(接收?頻):通知服務(wù)器端該客戶端是否要發(fā)送?頻。
receiveAudio命令結(jié)構(gòu)如下:

receiveVideo(接收視頻):通知服務(wù)器端該客戶端是否要發(fā)送視頻
receiveVideo命令結(jié)構(gòu)如下:

publish(推送數(shù)據(jù)):由客戶端向服務(wù)器發(fā)起請求推流到服務(wù)器。
publish命令結(jié)構(gòu)如下:

seek(定位流的位置):定位到視頻或?頻的某個位置,以毫秒為單位。
seek命令的結(jié)構(gòu)如下:

pause(暫停):客戶端告知服務(wù)端停?或恢復(fù)播放。
如果Pause為true即表示客戶端請求暫停的話,服務(wù)端暫停對應(yīng)的流會返回NetStream.Pause.Notify的onStatus命令來告知客戶端當(dāng)前流處于暫停的狀態(tài),當(dāng)Pause為false時,服務(wù)端會返回NetStream.Unpause.Notify的命令來告知客戶端當(dāng)前流恢復(fù)。如果服務(wù)端對該命令響應(yīng)失敗,返回_error信息。
pause命令的結(jié)構(gòu)如下:

5. 代表流程
推流流程
推流流程,在前面的文章已經(jīng)有了很深入的介紹。流媒體推拉流實(shí)戰(zhàn)之RTMP協(xié)議分析(BAT面試官推薦)
現(xiàn)貼下流程。如下圖:

拉流播放流程如下:

6. 新?學(xué)習(xí)建議
如果讀者仔細(xì)讀完了上?講的RTMP協(xié)議,想必會覺得RTMP協(xié)議?常繁瑣,事實(shí)也確實(shí)是這樣,RTMP協(xié)議中充斥著很多冗余的字段,?如三次握?中的時間戳的校對,還有?些特殊的命令,如FCPublish、UnFCPublish等,但在實(shí)際實(shí)現(xiàn)中為了保證更?兼容性通常還是要處理這些看似多余的命令。加上Adobe對RTMP協(xié)議的實(shí)現(xiàn)細(xì)節(jié)有些并沒有按照協(xié)議來或者協(xié)議中,沒有寫清楚??搞了?套實(shí)現(xiàn),其他應(yīng)?開發(fā)時還要兼容Adobe錯誤的實(shí)現(xiàn),從?使的RTMP也?直為開發(fā)者所詬病。但不管怎樣,RTMP確實(shí)提供了?種能夠全?并且實(shí)現(xiàn)簡單的協(xié)議來保證流信息的傳輸,這??暫時還沒有?種更完善更簡潔的協(xié)議能夠取代它在視頻流開發(fā)中的地位。新??開始接觸RTMP的時候肯定會覺得頭?,這也是RTMP協(xié)議不簡潔的后果。建議讀者在學(xué)習(xí)時先過?遍協(xié)議理解?概的概念和流程,然后對照wireshark抓的包,和協(xié)議進(jìn)??對,這樣將理論和實(shí)踐結(jié)合,應(yīng)該會理解的更快?點(diǎn)。
7.源碼參考
源碼主要來源如下開源庫:
(1). librtmp
(2). ffmpeg rtmppkt.h
(3). srs
csid是?定義的,主要不使?保留的0,1即可,?如ffmpeg的rtmppkt.h。對于chunk size的設(shè)置,ffmpeg保持和服務(wù)器?致。
/*** channels used to for RTMP packets with different purposes (i.e. da ta, network
* control, remote procedure calls, etc.) */
enum RTMPChannel {
RTMP_NETWORK_CHANNEL = 2, ///< channel for network-related mess ages (bandwidth report, ping, etc)
RTMP_SYSTEM_CHANNEL, ///< channel for sending server contr ol messages
RTMP_AUDIO_CHANNEL, ///< channel for audio data
RTMP_VIDEO_CHANNEL = 6, ///< channel for video data
RTMP_SOURCE_CHANNEL = 8, ///< channel for a/v invokes };
librtmp
RTMP_Write適合將FLV?件幀進(jìn)?發(fā)送,RTMP_Read適合?來dump RTMP碼流。
srs源碼
srs_kernel_flv.hpp
message type相關(guān)。
// 5. Protocol Control Messages
// RTMP reserves message type IDs 1-7 for protocol control messages. // These messages contain information needed by the RTM Chunk Stream // protocol or RTMP itself. Protocol messages with IDs 1 & 2 are
5 // reserved for usage with RTM Chunk Stream protocol. Protocol messa ges
6 // with IDs 3-6 are reserved for usage of RTMP. Protocol message wit h ID
7 // 7 is used between edge server and origin server.
8 #define RTMP_MSG_SetChunkSize 0x01
9 #define RTMP_MSG_AbortMessage 0x02
10 #define RTMP_MSG_Acknowledgement 0x03
11 #define RTMP_MSG_UserControlMessage 0x04
12 #define RTMP_MSG_WindowAcknowledgementSize 0x05
13 #define RTMP_MSG_SetPeerBandwidth 0x06
14 #define RTMP_MSG_EdgeAndOriginServerCommand 0x07
15 // 3. Types of messages
16 // The server and the client send messages over the network to
17 // communicate with each other. The messages can be of any type whic h
18 // includes audio messages, video messages, command messages, shared
19 // object messages, data messages, and user control messages.
20 // 3.1. Command message
21 // Command messages carry the AMF-encoded commands between the clien t
22 // and the server. These messages have been assigned message type va lue
23 // of 20 for AMF0 encoding and message type value of 17 for AMF3
24 // encoding. These messages are sent to perform some operations like
25 // connect, createStream, publish, play, pause on the peer. Command
26 // messages like onstatus, result etc. are used to inform the sender
27 // about the status of the requested commands. A command message 28 // consists of command name, transaction ID, and command object that 29 // contains related parameters. A client or a server can request Rem
srs_rtmp_stack.hpp
// The amf0 command message, command name macros
#define RTMP_AMF0_COMMAND_CONNECT "connect"
#define RTMP_AMF0_COMMAND_CREATE_STREAM "createStream"
#define RTMP_AMF0_COMMAND_CLOSE_STREAM "closeStream"
#define RTMP_AMF0_COMMAND_PLAY "play"
#define RTMP_AMF0_COMMAND_PAUSE "pause"
#define RTMP_AMF0_COMMAND_ON_BW_DONE "onBWDone"
#define RTMP_AMF0_COMMAND_ON_STATUS "onStatus"
#define RTMP_AMF0_COMMAND_RESULT "_result"
#define RTMP_AMF0_COMMAND_ERROR "_error"
#define RTMP_AMF0_COMMAND_RELEASE_STREAM "releaseStream"
#define RTMP_AMF0_COMMAND_FC_PUBLISH "FCPublish"
#define RTMP_AMF0_COMMAND_UNPUBLISH "FCUnpublish"
#define RTMP_AMF0_COMMAND_PUBLISH "publish"
#define RTMP_AMF0_DATA_SAMPLE_ACCESS "|RtmpSampleAccess"
// The signature for packets to client.
#define RTMP_SIG_FMS_VER "3,5,3,888"
#define RTMP_SIG_AMF0_VER 0
#define RTMP_SIG_CLIENT_ID "ASAICiss"
// The onStatus consts.#define StatusLevel "level"
#define StatusCode "code"
#define StatusDescription "description"
#define StatusDetails "details"
#define StatusClientId "clientid"
// The status value
#define StatusLevelStatus "status"
// The status error
#define StatusLevelError "error"
// The code value #define StatusCodeConnectSuccess "NetConnection.Conne ct.Success"
#define StatusCodeConnectRejected "NetConnection.Conne ct.Rejected"
#define StatusCodeStreamReset "NetStream.Play.Rese t"
#define StatusCodeStreamStart "NetStream.Play.Star t"
#define StatusCodeStreamPause "NetStream.Pause.Not ify"
#define StatusCodeStreamUnpause "NetStream.Unpause.N otify"
#define StatusCodePublishStart "NetStream.Publish.S tart"
#define StatusCodeDataStart "NetStream.Data.Star t"
#define StatusCodeUnpublishSuccess "NetStream.Unpublis h.Success"
重點(diǎn)參考一些比較好用源碼和有關(guān)RTMP的知識。
https://github.com/zkzszd/NativeCodec.git
https://github.com/Cooliodtryl/upstream-h.264-es-file-as-rtmp-.git
https://github.com/ireader/media-server
https://github.com/medooze/media-server
直播推流實(shí)現(xiàn)RTMP協(xié)議的?些注意事項: https://www.cnblogs.com/lidabo/p/7232594.html
RTMP 協(xié)議規(guī)范(中?版) :https://www.cnblogs.com/Kingfans/p/7083100.html
本篇文章就分享到這里,歡迎關(guān)注,點(diǎn)贊,收藏,轉(zhuǎn)發(fā),評論區(qū)討論 。
更多技術(shù)知識,歡迎關(guān)注 微信公眾號 “記錄世界 from antonio”