轉自《RTMP協議詳解(一) (二) (三) 》
Real Time Messaging Protocol(實時消息傳送協議協議)是Adobe Systems公司為Flash播放器和服務器之間音頻、視頻和數據傳輸開發的私有協議。
具體使用RTMP的AS代碼大概如下:
var videoInstance:Video = your_video_instance;
var nc:NetConnection = new NetConnection();
var connected:Boolean = nc.connect("rtmp://localhost/myapp");
var ns:NetStream = new NetStream(nc);
videoInstance.attachVideo(ns);
ns.play("flvName");
Adobe也在官方網站已經提供了RTMP協議的官方文檔說明,為什么要寫這個系列文章最大的原因只是對前一段工作的一個總結和回顧,最近兩個月,實現了一個RTMP Server的c++版本,把公司的流媒體服務和flash無縫對接起來。希望我的文字能給后來研究這個協議的同學有一定的幫助。
RTMP協議是一個基於TCP的高層協議族,當然這個玩意據說還有UDP協議版本的,不過現在還沒有出來,好像Adobe下一版本的FMS會提供支持。下文將要描述的是TCP協議版本的協議。
RTMP協議的概要理解:
RTMP協議是為了和flash之間交換信令以及媒體數據。為了提高使用效率信令和媒體數據都是使用相同的機制。因為是相同的機制Adobe就整出來了一些比較搞人的概念,當然每個協議第一次接觸都是比較難理解的。
在RTMP協議中信令和媒體數據都稱之為Message,在網絡中傳輸這些Message,為了區分它們肯定是要加一個Message head的,所以RTMP協議也有一個Message head,還有一個問題因為RTMP協議是基於TCP的,由於TCP的包長度是有限制的(一般來說不超過1500個字節),而RTMP的Message長度是有可能很大的,像一個視頻幀的包可能會有幾十甚至幾千K,這個問題就必然有一個分片的問題,在RTMP協議中對應的說法就是chunk,每一個Message + head都是由一個和多個chunk組成的。到這里對RTMP協議的概要理解就算完了。
RTMP的字節序:
RTMP的字節序和大多數網絡協議一樣是大端序,也有一些字段是小端序的,不過都有特殊的說明。
RTMP的head組成
RTMP的head在協議中的表現形式是chunk head,前面已經說到一個Message + head可以分成一個和多個chunk,為了區分這些chunk,肯定是需要一個chunk head的,具體的實現就把Message head的信息和chunk head的信息合並在一起以chunk head的形式表現。
一個完整的chunk的組成如下圖所示
Chunk basic header:
該字段包含chunk的stream ID和 type 。chunk的Type決定了消息頭的編碼方式。該字段的長度完全依賴於stream ID,該字段是一個可變長的字段。
Chunk Msg Header:0, 3 ,7, 11
該字段包含了將要發送的消息的信息(或者是一部分,一個消息拆成多個chunk的情況下是一部分)該字段的長度由chunk basic header中的type決定。
Extend Timestamp: 0 ,4 bytes
該字段發送的時候必須是正常的時間戳設置成0xffffff時,當正常時間戳不為0xffffff時,該字段不發送。當時間戳比0xffffff小該字段不發送,當時間戳比0xffffff大時該字段必須發送,且正常時間戳設置成0xffffff。
Chunk Data
實際數據(Payload),可以是信令,也可以是媒體數據。
Chunk basic header:
chunk basic head的長度為1~3個字節,具體長度主要是依賴chunk stream ID的長度,所謂chunk stream ID是flash server用來管理連接的客戶端的信令交互的標識,在red5的文檔中稱之為channel ID,協議最大支持65597個streamID 從3~65599。ID 0,1,2為協議保留,0代表ID是64~319(第二個byte + 64);1代表chunk stream ID為64~65599((第三個byte)* 256 + 第二個byte + 64)(小端表示);2代表該消息為低層的協議(在RTMP協議中控制信令的chunk stream ID都是2)。3~63的chunk stream ID就是該byte的值。沒有附加的字段來標識chunk stream streamID。在這里要指出的是雖然RTMP的chunk stream ID理論是可以達到65599,但是目前使用的chunk stream ID很少,2~7都是約定的,8是用來傳輸publish play等命令,其他的chunk stream ID目前好像沒有使用,至少我不知道用來干嘛的。
所以目前chunk basic head的長度一般為1個字節。這一個字節由兩部分組成
+++++++++++++++++++
+fmt + cs id +
+++++++++++++++++++
fmt占兩個bit用來標識緊跟其后的chunk Msg Header的長度,cs id占六個bit。
兩位的fmt取值為 0~3,分別代表的意義如下:
case 0:chunk Msg Header長度為11;
case 1:chunk Msg Header長度為7;
case 2:chunk Msg Header長度為3;
case 3:chunk Msg Header長度為0;
所以 只有一個字節的chunk basic header取值為 chunk basic header = (fmt << 6) | (cs id).
Chunk Msg Header:
Chunk Msg Header的長度是可變的,Chunk Msg Header可變的原因是為了壓縮傳輸的字節數,把一些相同類型的chunk的head去掉一些字節,換句話說就是四種類型的包頭都可以通過一定的規則還原成11個字節,這個壓縮和還原在RTMP協議中稱之為復用/解復用。
那我們以11個字節的完整包頭來解釋Chunk Msg Header,如圖所示
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ timestamp + message length + message type id + message stream id +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Timestamp:3bytes
對於type 0的chunk,絕對時間戳在這里表示,如果時間戳值大於等於0xffffff(16777215),該值必須是0xffffff,且時間戳擴展字段必須發送,其他情況沒有要求。
message length:3bytes
Message的長度,注意這里的長度並不是跟隨chunk head其后的chunk data(Payload)的長度,而是前文提到的一條信令或者一幀視頻數據或音頻數據的長度。前文提到過信令或者媒體數據都稱之為Message,一條Message可以分為一條或者多條chunk。
message type id:1byte
Message的類型ID,具體的值將在后文專門來討論。
message stream id:4bytes
message stream id的字節序是小端序,這個字段是為了解復用而設計的,RTMP文檔上說的相當的模糊,
message stream ID可以使任意值,不同的消息流復用成相同的chunk stream,基於它們的ID能夠解復用。於chunk stream 是相關的,這個字段是一個不透明的值沒有整明白什么意思,我的理解就是用來標識和服務器連接的flash端的序號。
長度是7 bytes 的chunk head,該類型不包含stream ID,該chunk的streamID和前一個chunk的stream ID是相同的,變長的消息,例如視頻流格式,在第一個新的chunk以后使用這種類型,注意其中時間戳部分是相對時間,為何上一個絕對時間之間的差值 如圖所示:
++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ timestamp delta + message length + message type id +
++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 bytes的chunk head,該類型既不包含stream ID 也不包含消息長度,這種類型用於stream ID和前一個chunk相同,且有固定長度的信息,例如音頻流格式,在第一個新的chunk以后使用該類型。如圖所示:
++++++++++++++++++++
+ timestamp delta +
++++++++++++++++++++
0 bytes的chunk head,這種類型的chunk從前一個chunk得到值信息,當一個單個消息拆成多個chunk時,這些chunk除了第一個以外,其他的都應該使用這種類型,
chunk的長度:
chunk的長度初始長度固定為128個字節,但是這個值並不是不可變的,在客戶端和服務端建立連接以后,客戶端和服務端都可以通過發送信令的方式來通知對端修改chunk的長度,理論上來說可以修改chunk的最長長度為65536。這里chunk的長度是指chunk的數據部分的長度,即chunk data(payload)的長度,如果一條Message的數據長度超過了chunk的長度,就必須把Message分割成多條chunk,即如果一條視頻類型Message長度為2000個byte,chunk長度為1500,則該Message將會分割成兩條chunk,第一條的chunk data長度為1500,第二條的chunk data長度為500。當然這兩條chunk的chunk head肯定是不同的,其中第二條chunk的chunk head就是0字節的。