RTMP協(xié)議是Real Time Message Protocol(實時信息傳輸協(xié)議)的縮寫,它是由Adobe公司提出的一種應(yīng)用層的協(xié)議,用來解決多媒體數(shù)據(jù)傳輸流的多路復(fù)用(Multiplexing)和分包(packetizing)的問題。隨著VR技術(shù)的發(fā)展,視頻直播等領(lǐng)域逐漸活躍起來,RTMP作為業(yè)內(nèi)廣泛使用的協(xié)議也重新被相關(guān)開發(fā)者重視起來。正好最近在從事這方面的工作昧廷,在此記錄下自己對RTMP的理解,文章內(nèi)容多翻譯自英文版RTMP文檔偎箫,按照本人的理解重新整理木柬,希望可以幫助想要了解RTMP協(xié)議的朋友,也方面自己日后查閱淹办。
1. 總體介紹:
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ù)拿钚畔ⅰTMP協(xié)議傳輸時會對數(shù)據(jù)做自己的格式化恐疲,這種格式的消息我們稱之為RTMP Message腊满,而實際傳輸?shù)臅r候為了更好地實現(xiàn)多路復(fù)用、分包和信息的公平性培己,發(fā)送端會把Message劃分為帶有Message ID的Chunk碳蛋,每個Chunk可能是一個單獨的Message,也可能是Message的一部分省咨,在接受端會根據(jù)chunk中包含的data的長度肃弟,message id和message的長度把chunk還原成完整的Message,從而實現(xiàn)信息的收發(fā)零蓉。
2. 握手
要建立一個有效的RTMP Connection鏈接笤受,首先要“握手”:客戶端要向服務(wù)器發(fā)送C0,C1,C2(按序)三個chunk,服務(wù)器向客戶端發(fā)送S0,S1,S2(按序)三個chunk敌蜂,然后才能進行有效的信息傳輸感论。RTMP協(xié)議本身并沒有規(guī)定這6個Message的具體傳輸順序,但RTMP協(xié)議的實現(xiàn)者需要保證這幾點:
客戶端要等收到S1之后才能發(fā)送C2
客戶端要等收到S2之后才能發(fā)送其他信息(控制信息和真實音視頻等數(shù)據(jù))
服務(wù)端要等到收到C0之后發(fā)送S1
服務(wù)端必須等到收到C1之后才能發(fā)送S2
服務(wù)端必須等到收到C2之后才能發(fā)送其他信息(控制信息和真實音視頻等數(shù)據(jù))
如果每次發(fā)送一個握手chunk的話握手順序會是這樣:
理論上來講只要滿足以上條件紊册,如何安排6個Message的順序都是可以的,但實際實現(xiàn)中為了在保證握手的身份驗證功能的基礎(chǔ)上盡量減少通信的次數(shù),一般的發(fā)送順序是這樣的囊陡,這一點可以通過wireshark抓ffmpeg推流包進行驗證:
|client|Server |
|---C0+C1—->|
|<--S0+S1+S2– |
|---C2----> |
3. RTMP Chunk Stream
Chunk Stream是對傳輸RTMP Chunk的流的邏輯上的抽象芳绩,客戶端和服務(wù)器之間有關(guān)RTMP的信息都在這個流上通信。這個流上的操作也是我們關(guān)注RTMP協(xié)議的重點撞反。
3.1 Message(消息)
這里的Message是指滿足該協(xié)議格式的妥色、可以切分成Chunk發(fā)送的消息,消息包含的字段如下:
Timestamp(時間戳):消息的時間戳(但不一定是當(dāng)前時間遏片,后面會介紹)嘹害,4個字節(jié)
Length(長度):是指Message Payload(消息負(fù)載)即音視頻等信息的數(shù)據(jù)的長度,3個字節(jié)
TypeId(類型Id):消息的類型Id吮便,1個字節(jié)
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僚匆。每個Chunk中帶有MessageID代表屬于哪個Message微渠,接受端也會按照這個id來將chunk組裝成Message。
為什么RTMP要將Message拆分成不同的Chunk呢咧擂?通過拆分逞盆,數(shù)據(jù)量較大的Message可以被拆分成較小的“Message”,這樣就可以避免優(yōu)先級低的消息持續(xù)發(fā)送阻塞優(yōu)先級高的數(shù)據(jù)松申,比如在視頻的傳輸過程中云芦,會包括視頻幀,音頻幀和RTMP控制信息攻臀,如果持續(xù)發(fā)送音頻數(shù)據(jù)或者控制數(shù)據(jù)的話可能就會造成視頻幀的阻塞焕数,然后就會造成看視頻時最煩人的卡頓現(xiàn)象。同時對于數(shù)據(jù)量較小的Message刨啸,可以通過對Chunk Header的字段來壓縮信息堡赔,從而減少信息的傳輸量。(具體的壓縮方式會在后面介紹)
Chunk的默認(rèn)大小是128字節(jié)设联,在傳輸過程中善已,通過一個叫做Set Chunk Size的控制信息可以設(shè)置Chunk數(shù)據(jù)量的最大值,在發(fā)送端和接受端會各自維護一個Chunk Size离例,可以分別設(shè)置這個值來改變自己這一方發(fā)送的Chunk的最大大小换团。大一點的Chunk減少了計算每個chunk的時間從而減少了CPU的占用率,但是它會占用更多的時間在發(fā)送上宫蛆,尤其是在低帶寬的網(wǎng)絡(luò)情況下艘包,很可能會阻塞后面更重要信息的傳輸的猛。小一點的Chunk可以減少這種阻塞問題,但小的Chunk會引入過多額外的信息(Chunk中的Header)想虎,少量多次的傳輸也可能會造成發(fā)送的間斷導(dǎo)致不能充分利用高帶寬的優(yōu)勢卦尊,因此并不適合在高比特率的流中傳輸。在實際發(fā)送時應(yīng)對要發(fā)送的數(shù)據(jù)用不同的Chunk Size去嘗試舌厨,通過抓包分析等手段得出合適的Chunk大小岂却,并且在傳輸過程中可以根據(jù)當(dāng)前的帶寬信息和實際信息的大小動態(tài)調(diào)整Chunk的大小,從而盡量提高CPU的利用率并減少信息的阻塞機率裙椭。
3.3 Chunk Format(塊格式)
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é)議支持用戶自定義[3氓栈,65599]之間的CSID渣磷,0,1授瘦,2由協(xié)議保留表示特殊信息醋界。0代表Basic Header總共要占用2個字節(jié),CSID在[64提完,319]之間形纺,1代表占用3個字節(jié),CSID在[64徒欣,65599]之間逐样,2代表該chunk是控制信息和一些命令信息,后面會有詳細(xì)的介紹打肝。
chunk type的長度固定為2位脂新,因此CSID的長度是(6=8-2)、(14=16-2)粗梭、(22=24-2)中的一個争便。
當(dāng)Basic Header為1個字節(jié)時,CSID占6位断医,6位最多可以表示64個數(shù)滞乙,因此這種情況下CSID在[0奏纪,63]之間,其中用戶可自定義的范圍為[3酷宵,63]亥贸。
當(dāng)Basic Header為2個字節(jié)時,CSID占14位浇垦,此時協(xié)議將與chunk type所在字節(jié)的其他位都置為0,剩下的一個字節(jié)來表示CSID-64荣挨,這樣共有8個二進制位來存儲CSID男韧,8位可以表示[0,255]共256個數(shù)默垄,因此這種情況下CSID在[64此虑,319],其中319=255+64口锭。
當(dāng)Basic Header為3個字節(jié)時朦前,CSID占22位,此時協(xié)議將[2鹃操,8]字節(jié)置為1韭寸,余下的16個字節(jié)表示CSID-64,這樣共有16個位來存儲CSID荆隘,16位可以表示[0恩伺,65535]共65536個數(shù),因此這種情況下CSID在[64椰拒,65599]晶渠,其中65599=65535+64,需要注意的是燃观,Basic Header是采用小端存儲的方式褒脯,越往后的字節(jié)數(shù)量級越高,因此通過這3個字節(jié)每一位的值來計算CSID時缆毁,應(yīng)該是:<第三個字節(jié)的值>x256+<第二個字節(jié)的值>+64
可以看到2個字節(jié)和3個字節(jié)的Basic Header所能表示的CSID是有交集的[64番川,319],但實際實現(xiàn)時還是應(yīng)該秉著最少字節(jié)的原則使用2個字節(jié)的表示方式來表示[64,319]的CSID珊皿。
3.3.2 Message Header(消息的頭信息):
包含了要發(fā)送的實際信息(可能是完整的擒权,也可能是一部分)的描述信息。Message Header的格式和長度取決于Basic Header的chunk type适篙,共有4種不同的格式,由上面所提到的Basic Header中的fmt字段控制箫爷。其中第一種格式可以表示其他三種表示的所有數(shù)據(jù)嚷节,但由于其他三種格式是基于對之前chunk的差量化的表示聂儒,因此可以更簡潔地表示相同的數(shù)據(jù),實際使用的時候還是應(yīng)該采用盡量少的字節(jié)表示相同意義的數(shù)據(jù)硫痰。以下按照字節(jié)數(shù)從多到少的順序分別介紹這4種格式的Message Header衩婚。
Type=0:
type=0時Message Header占用11個字節(jié),其他三種能表示的數(shù)據(jù)它都能表示效斑,但在chunk stream的開始的第一個chunk和頭信息中的時間戳后退(即值與上一個chunk相比減小非春,通常在回退播放的時候會出現(xiàn)這種情況)的時候必須采用這種格式。
timestamp(時間戳):占用3個字節(jié)缓屠,因此它最多能表示到16777215=0xFFFFFF=2
24-1, 當(dāng)它的值超過這個最大值時奇昙,這三個字節(jié)都置為1,這樣實際的timestamp會轉(zhuǎn)存到Extended Timestamp字段中敌完,接受端在判斷timestamp字段24個位都為1時就會去Extended timestamp中解析實際的時間戳储耐。
message length(消息數(shù)據(jù)的長度):占用3個字節(jié),表示實際發(fā)送的消息的數(shù)據(jù)如音頻幀滨溉、視頻幀等數(shù)據(jù)的長度什湘,單位是字節(jié)。注意這里是Message的長度晦攒,也就是chunk屬于的Message的總數(shù)據(jù)長度闽撤,而不是chunk本身Data的數(shù)據(jù)的長度。
message type id(消息的類型id):占用1個字節(jié)勤家,表示實際發(fā)送的數(shù)據(jù)的類型腹尖,如8代表音頻數(shù)據(jù)、9代表視頻數(shù)據(jù)伐脖。
msg stream id(消息的流id):占用4個字節(jié)热幔,表示該chunk所在的流的ID,和Basic Header的CSID一樣讼庇,它采用小端存儲的方式绎巨,
Type = 1:
type=1時Message Header占用7個字節(jié),省去了表示msg stream id的4個字節(jié)蠕啄,表示此chunk和上一次發(fā)的chunk所在的流相同场勤,如果在發(fā)送端只和對端有一個流鏈接的時候可以盡量去采取這種格式。
timestamp delta:占用3個字節(jié)歼跟,注意這里和type=0時不同和媳,存儲的是和上一個chunk的時間差。類似上面提到的timestamp哈街,當(dāng)它的值超過3個字節(jié)所能表示的最大值時留瞳,三個字節(jié)都置為1,實際的時間戳差值就會轉(zhuǎn)存到Extended Timestamp字段中骚秦,接受端在判斷timestamp delta字段24個位都為1時就會去Extended timestamp中解析時機的與上次時間戳的差值她倘。
Type = 2:
type=2時Message Header占用3個字節(jié)璧微,相對于type=1格式又省去了表示消息長度的3個字節(jié)和表示消息類型的1個字節(jié),表示此chunk和上一次發(fā)送的chunk所在的流硬梁、消息的長度和消息的類型都相同前硫。余下的這三個字節(jié)表示timestamp delta,使用同type=1
Type = 3
0字節(jié)S埂R俚纭!好吧罩息,它表示這個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
3.3.3 Extended Timestamp(擴展時間戳):
上面我們提到在chunk中會有時間戳timestamp和時間戳差timestamp delta驶悟,并且它們不會同時存在胡野,只有這兩者之一大于3個字節(jié)能表示的最大數(shù)值0xFFFFFF=16777215時,才會用這個字段來表示真正的時間戳痕鳍,否則這個字段為0硫豆。擴展時間戳占4個字節(jié),能表示的最大數(shù)值就是0xFFFFFFFF=4294967295笼呆。當(dāng)擴展時間戳啟用時熊响,timestamp字段或者timestamp delta要全置為1,表示應(yīng)該去擴展時間戳字段來提取真正的時間戳或者時間戳差诗赌。注意擴展時間戳存儲的是完整值汗茄,而不是減去時間戳或者時間戳差的值。
3.3.4 Chunk Data(塊數(shù)據(jù)):
用戶層面上真正想要發(fā)送的與協(xié)議無關(guān)的數(shù)據(jù)境肾,長度在(0,chunkSize]之間剔难。
3.3.5 chunk表示例1
首先包含第一個Message的chunk的Chunk Type為0胆屿,因為它沒有前面可參考的chunk,timestamp為1000偶宫,表示時間戳非迹。type為0的header占用11個字節(jié),假定chunkstreamId為3<127纯趋,因此Basic Header占用1個字節(jié)憎兽,再加上Data的32個字節(jié),因此第一個chunk共44=11+1+32個字節(jié)吵冒。
第二個chunk和第一個chunk的CSID纯命,TypeId,Data的長度都相同痹栖,因此采用Chunk Type=2亿汞,timestamp delta=1020-1000=20,因此第二個chunk占用36=3+1+32個字節(jié)揪阿。
第三個chunk和第二個chunk的CSID疗我,TypeId,Data的長度和時間戳差都相同南捂,因此采用Chunk Type=3省去全部Message Header的信息吴裤,占用33=1+32個字節(jié)。
第四個chunk和第三個chunk情況相同溺健,也占用33=1+32個字節(jié)麦牺。
最后實際發(fā)送的chunk如下:
3.3.6 chunk表示例2
注意到Data的Length=307>128,因此這個Message要切分成幾個chunk發(fā)送,第一個chunk的Type=0鞭缭,Timestamp=1000剖膳,承擔(dān)128個字節(jié)的Data,因此共占用140=11+1+128個字節(jié)缚去。
第二個chunk也要發(fā)送128個字節(jié)潮秘,其他字段也同第一個chunk,因此采用Chunk Type=3易结,此時時間戳也為1000枕荞,共占用129=1+128個字節(jié)。
第三個chunk要發(fā)送的Data的長度為307-128-128=51個字節(jié)搞动,還是采用Type=3躏精,共占用1+51=52個字節(jié)。
最后實際發(fā)送的chunk如下:
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 Chunk Size(Message Type ID=1):設(shè)置chunk中Data字段所能承載的最大字節(jié)數(shù),默認(rèn)為128B攒霹,通信過程中可以通過發(fā)送該消息來設(shè)置chunk Size的大星影獭(不得小于128B),而且通信雙方會各自維護一個chunkSize催束,兩端的chunkSize是獨立的集峦。比如當(dāng)A想向B發(fā)送一個200B的Message,但默認(rèn)的chunkSize是128B抠刺,因此就要將該消息拆分為Data分別為128B和72B的兩個chunk發(fā)送少梁,如果此時先發(fā)送一個設(shè)置chunkSize為256B的消息,再發(fā)送Data為200B的chunk矫付,本地不再劃分Message,B接受到Set Chunk Size的協(xié)議控制消息時會調(diào)整的接受的chunk的Data的大小第焰,也不用再將兩個chunk組成為一個Message买优。
以下為代表Set Chunk Size消息的chunk的Data:
其中第一位必須為0,chunk Size占31個位挺举,最大可代表2147483647=0x7FFFFFFF=231-1杀赢,但實際上所有大于16777215=0xFFFFFF的值都用不上,因為chunk size不能大于Message的長度湘纵,表示Message的長度字段是用3個字節(jié)表示的脂崔,最大只能為0xFFFFFF。
Abort Message(Message Type ID=2):當(dāng)一個Message被切分為多個chunk梧喷,接受端只接收到了部分chunk時砌左,發(fā)送該控制消息表示發(fā)送端不再傳輸同Message的chunk,接受端接收到這個消息后要丟棄這些不完整的chunk铺敌。Data數(shù)據(jù)中只需要一個CSID汇歹,表示丟棄該CSID的所有已接收到的chunk。
Acknowledgement(Message Type ID=3):當(dāng)收到對端的消息大小等于窗口大谐テ尽(Window Size)時接受端要回饋一個ACK給發(fā)送端告知對方可以繼續(xù)發(fā)送數(shù)據(jù)产弹。窗口大小就是指收到接受端返回的ACK前最多可以發(fā)送的字節(jié)數(shù)量,返回的ACK中會帶有從發(fā)送上一個ACK后接收到的字節(jié)數(shù)弯囊。
Window Acknowledgement Size(Message Type ID=5):發(fā)送端在接收到接受端返回的兩個ACK間最多可以發(fā)送的字節(jié)數(shù)痰哨。
Set Peer Bandwidth(Message Type ID=6):限制對端的輸出帶寬胶果。接受端接收到該消息后會通過設(shè)置消息中的Window ACK Size來限制已發(fā)送但未接受到反饋的消息的大小來限制發(fā)送端的發(fā)送帶寬。如果消息中的Window ACK Size與上一次發(fā)送給發(fā)送端的size不同的話要回饋一個Window Acknowledgement Size的控制消息斤斧。
Hard(Limit Type=0):接受端應(yīng)該將Window Ack Size設(shè)置為消息中的值
Soft(Limit Type=1):接受端可以講Window Ack Size設(shè)為消息中的值早抠,也可以保存原來的值(前提是原來的Size小與該控制消息中的Window Ack Size)
Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit Type為0,本次也按Hard處理折欠,否則忽略本消息贝或,不去設(shè)置Window Ack Size。
4. 不同類型的RTMP Message
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.
Data Message(數(shù)據(jù)消息罐寨,Message Type ID=15或18):傳遞一些元數(shù)據(jù)(MetaData靡挥,比如視頻名,分辨率等等)或者用戶自定義的一些消息鸯绿。當(dāng)信息使用AMF0編碼時跋破,Message Type ID=18,AMF3編碼時Message Type ID=15.
Shared Object Message(共享消息瓶蝴,Message Type ID=16或19):表示一個Flash類型的對象毒返,由鍵值對的集合組成,用于多客戶端舷手,多實例時使用拧簸。當(dāng)信息使用AMF0編碼時,Message Type ID=19男窟,AMF3編碼時Message Type ID=16.
Audio Message(音頻信息盆赤,Message Type ID=8):音頻數(shù)據(jù)。
Video Message(視頻信息蝎宇,Message Type ID=9):視頻數(shù)據(jù)弟劲。
Aggregate Message (聚集信息,Message Type ID=22):多個RTMP子消息的集合
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读整。
———下面對以上7種信息具體介紹———-
4.1 Command Message(命令消息,Message Type ID=17或20)
發(fā)送端發(fā)送時會帶有命令的名字咱娶,如connect米间,TransactionID表示此次命令的標(biāo)識,Command Object表示相關(guān)參數(shù)膘侮。接受端收到命令后屈糊,會返回以下三種消息中的一種:_result 消息表示接受該命令,對端可以繼續(xù)往下執(zhí)行流程琼了,_error消息代表拒絕該命令要執(zhí)行的操作逻锐,method name消息代表要在之前命令的發(fā)送端執(zhí)行的函數(shù)名稱。這三種回應(yīng)的消息都要帶有收到的命令消息中的TransactionId來表示本次的回應(yīng)作用于哪個命令雕薪。
可以認(rèn)為發(fā)送命令消息的對象有兩種昧诱,一種是NetConnection,表示雙端的上層連接所袁,一種是NetStream盏档,表示流信息的傳輸通道,控制流信息的狀態(tài)燥爷,如Play播放流妆丘,Pause暫停。
4.1.1 NetConnection Commands(連接層的命令)
用來管理雙端之間的連接狀態(tài)局劲,同時也提供了異步遠(yuǎn)程方法調(diào)用(RPC)在對端執(zhí)行某方法,以下是常見的連接層的命令:
4.1.1.1 connect:用于客戶端向服務(wù)器發(fā)送連接請求奶赠,消息的結(jié)構(gòu)如下:
字段類型說明
Command Name(命令名字)String命令的名字鱼填,如”connect”
Transaction ID(事務(wù)ID)Number恒為1
Command Object(命令包含的參數(shù)對象)Object鍵值對集合表示的命令參數(shù)
Optional User Arguments(額外的用戶參數(shù))Object用戶自定義的額外信息
第三個字段中的Command Object中會涉及到很多鍵值對,這里不再一一列出毅戈,使用時可以參考協(xié)議的官方文檔苹丸。
消息的回應(yīng)有兩種,_result表示接受連接苇经,_error表示連接失敗
4.1.1.2 Call:用于在對端執(zhí)行某函數(shù)赘理,即常說的RPC:遠(yuǎn)程進程調(diào)用,消息的結(jié)構(gòu)如下:
字段類型說明
Procedure Name(進程名)String要調(diào)用的進程名稱
Transaction IDNumber|如果想要對端響應(yīng)的話置為非0值扇单,否則置為0
Command ObjectObject命令參數(shù)
Optional ArguentsObject用戶自定義參數(shù)
如果消息中的TransactionID不為0的話商模,對端需要對該命令做出響應(yīng),響應(yīng)的消息結(jié)構(gòu)如下:
字段類型說明
Command Name(命令名)String命令的名稱
TransactionIDNumber上面接收到的命令消息中的TransactionID
Command ObjectObject命令參數(shù)
Optional ArgumentsObject用戶自定義參數(shù)
4.1.1.3 Create Stream:創(chuàng)建傳遞具體信息的通道,從而可以在這個流中傳遞具體信息施流,傳輸信息單元為Chunk响疚。
字段類型說明
Command Name(命令名)String“createStream”
TransactionIDNumber上面接收到的命令消息中的TransactionID
Command ObjectObject命令參數(shù)
Optional ArgumentsObject用戶自定義參數(shù)
4.1.2 NetStream Commands(流連接上的命令)
Netstream建立在NetConnection之上,通過NetConnection的createStream命令創(chuàng)建瞪醋,用于傳輸具體的音頻忿晕、視頻等信息。在傳輸層協(xié)議之上只能連接一個NetConnection银受,但一個NetConnection可以建立多個NetStream來建立不同的流通道傳輸數(shù)據(jù)践盼。
以下會列出一些常用的NetStream Commands,服務(wù)端收到命令后會通過onStatus的命令來響應(yīng)客戶端宾巍,表示當(dāng)前NetStream的狀態(tài)咕幻。
onStatus命令的消息結(jié)構(gòu)如下:
字段類型說明
Command NameString“onStatus”
TransactionIDNumber恒為0
Command ObjectNULL對onSatus命令來說不需要這個字段
Info ObjectObjectAMF類型的Object,至少包含以下三個屬性:1蜀漆,“l(fā)evel”谅河,String類型,可以為“warning”确丢、”status”绷耍、”error”中的一種;2鲜侥,”code”,String類型褂始,代表具體狀態(tài)的關(guān)鍵字,比如”NetStream.Play.Start”表示開始播流;3描函,”description”崎苗,String類型,代表對當(dāng)前狀態(tài)的描述舀寓,提供對當(dāng)前狀態(tài)可讀性更好的解釋胆数,除了這三種必要信息,用戶還可以自己增加自定義的鍵值對
4.1.2.1 play(播放):由客戶端向服務(wù)器發(fā)起請求從服務(wù)器端接受數(shù)據(jù)(如果傳輸?shù)男畔⑹且曨l的話就是請求開始播流)互墓,可以多次調(diào)用必尼,這樣本地就會形成一組數(shù)據(jù)流的接收者。注意其中有一個reset字段篡撵,表示是覆蓋之前的播流(設(shè)為true)還是重新開始一路播放(設(shè)為false)判莉。
play命令的結(jié)構(gòu)如下:
字段類型說明
命令名String“play”
事務(wù)IDNumber恒為0
命令參數(shù)對象Null不需要此字段,設(shè)為空
流名稱String要播放的流的名稱
開始位置Number可選參數(shù)育谬,表示從何時開始播流券盅,以秒為單位。默認(rèn)為-2膛檀,代表選取對應(yīng)該流名稱的直播流锰镀,即當(dāng)前正在推送的流開始播放娘侍,如果對應(yīng)該名稱的直播流不存在,就選取該名稱的流的錄播版本互站,如果這也沒有私蕾,當(dāng)前播流端要等待直到對端開始該名稱的流的直播。如果傳值-1胡桃,那么只會選取直播流進行播放踩叭,即使有錄播流也不會播放;如果傳值或者正數(shù)翠胰,就代表從該流的該時間點開始播放容贝,如果流不存在的話就會自動播放播放列表中的下一個流
周期Number可選參數(shù),表示回退的最小間隔單位之景,以秒為單位計數(shù)斤富。默認(rèn)值為-1,代表直到直播流不再可用或者錄播流停止后才能回退播放锻狗;如果傳值為0满力,代表從當(dāng)前幀開始播放
重置Boolean可選參數(shù),true代表清除之前的流轻纪,重新開始一路播放油额,false代表保留原來的流,向本地的播放列表中再添加一條播放流
4.1.2.2 play2(播放):和上面的play命令不同的是刻帚,play2命令可以將當(dāng)前正在播放的流切換到同樣數(shù)據(jù)但不同比特率的流上潦嘶,服務(wù)器端會維護多種比特率的文件來供客戶端使用play2命令來切換。
字段類型說明
Command NameString“play2”
TransactionIDNumber恒為0
Command ObjectNULL,對onSatus命令來說不需要這個字段
parametersObjectAMF編碼的Flash對象崇众,包括了一些用于描述flash.net.NetstreamPlayOptions ActionScript obejct的參數(shù)
4.1.2.3 deleteStream(刪除流):用于客戶端告知服務(wù)器端本地的某個流對象已被刪除掂僵,不需要再傳輸此路流。
字段類型說明
Command NameString“deleteStream”
TransactionIDNumber恒為0
Command ObjectNULL,對onSatus命令來說不需要這個字段
Stream ID(流ID)Number本地已刪除顷歌,不再需要服務(wù)器傳輸?shù)牧鞯腎D
4.1.2.4 receiveAudio(接收音頻):通知服務(wù)器端該客戶端是否要發(fā)送音頻
receiveAudio命令結(jié)構(gòu)如下:
字段類型說明
Command NameString“receiveAudio”
TransactionIDNumber恒為0
Command ObjectNULL對onSatus命令來說不需要這個字段
Bool FlagBooleantrue表示發(fā)送音頻锰蓬,如果該值為false,服務(wù)器端不做響應(yīng)眯漩,如果為true的話互妓,服務(wù)器端就會準(zhǔn)備接受音頻數(shù)據(jù),會向客戶端回復(fù)NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客戶端當(dāng)前流的狀態(tài)
4.1.2.5 receiveVideo(接收視頻):通知服務(wù)器端該客戶端是否要發(fā)送視頻
receiveVideo命令結(jié)構(gòu)如下:
字段類型說明
Command NameString“receiveVideo”
TransactionIDNumber恒為0
Command ObjectNULL對onSatus命令來說不需要這個字段
Bool FlagBooleantrue表示發(fā)送視頻坤塞,如果該值為false,服務(wù)器端不做響應(yīng)澈蚌,如果為true的話摹芙,服務(wù)器端就會準(zhǔn)備接受視頻數(shù)據(jù),會向客戶端回復(fù)NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客戶端當(dāng)前流的狀態(tài)
4.1.2.6 publish(推送數(shù)據(jù)):由客戶端向服務(wù)器發(fā)起請求推流到服務(wù)器宛瞄。
publish命令結(jié)構(gòu)如下:
字段類型說明
Command NameString“publish”
TransactionIDNumber恒為0
Command ObjectNULL,對onSatus命令來說不需要這個字段
Publishing Name(推流的名稱)String流名稱|
Publishing Type(推流類型)String“l(fā)ive”浮禾、”record”交胚、”append”中的一種。live表示該推流文件不會在服務(wù)器端存儲盈电;record表示該推流的文件會在服務(wù)器應(yīng)用程序下的子目錄下保存以便后續(xù)播放蝴簇,如果文件已經(jīng)存在的話刪除原來所有的內(nèi)容重新寫入;append也會將推流數(shù)據(jù)保存在服務(wù)器端匆帚,如果文件不存在的話就會建立一個新文件寫入熬词,如果對應(yīng)該流的文件已經(jīng)存在的話保存原來的數(shù)據(jù),在文件末尾接著寫入
4.1.2.7 seek(定位流的位置):定位到視頻或音頻的某個位置吸重,以毫秒為單位互拾。
seek命令的結(jié)構(gòu)如下:
字段類型說明
Command NameString“seek”
TransactionIDNumber恒為0
Command ObjectNULL,對onSatus命令來說不需要這個字段
milliSecondsNumber定位到該文件的xx毫秒處|
4.1.2.8 pause(暫停):客戶端告知服務(wù)端停止或恢復(fù)播放。
pause命令的結(jié)構(gòu)如下:
字段類型說明
Command NameString“pause”
TransactionIDNumber恒為0
Command ObjectNULL,對onSatus命令來說不需要這個字段
Pause/Unpause FlagBooleantrue表示暫停嚎幸,false表示恢復(fù)
milliSecondsNumber暫脱湛螅或者恢復(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信息。
5. 代表流程
5.1 推流流程
5.2 播流流程
6. 新手建議
如果讀者仔細(xì)讀完了上面講的RTMP協(xié)議舶担,想必會覺得RTMP協(xié)議非常繁瑣坡疼,事實也確實是這樣,RTMP協(xié)議中充斥著很多冗余的字段衣陶,比如三次握手中的時間戳的校對柄瑰,還有一些特殊的命令,如FCPublish剪况、UnFCPublish等教沾,但在實際實現(xiàn)中為了保證更大兼容性通常還是要處理這些看似多余的命令。加上Adobe對RTMP協(xié)議的實現(xiàn)細(xì)節(jié)有些并沒有按照協(xié)議來或者協(xié)議中沒有寫清楚自己搞了一套實現(xiàn)译断,其他應(yīng)用開發(fā)時還要兼容Adobe錯誤的實現(xiàn)授翻,從而使的RTMP也一直為開發(fā)者所詬病。但不管怎樣孙咪,RTMP確實提供了一種能夠全面并且實現(xiàn)簡單的協(xié)議來保證流信息的傳輸堪唐,這方面暫時還沒有一種更完善更簡潔的協(xié)議能夠取代它在視頻流開發(fā)中的地位。