RTMP協議分析
一、RTMP包頭
RTMP協議 封包 參考Red5
RTMP協議封包 由一個包頭和一個包體組成,包頭可以是4種長度的任意一種:12, 8, 4, 1 byte(s).完整的RTMP包頭應該是12bytes,包含了時間戳,Head_Type,AMFSize,AMFType,StreamID資訊, 8位元組的包頭只紀錄了時間戳,Head_Type,AMFSize,AMFType, 4個位元組的包頭記錄了時間戳,Head_Type。1個位元組的包頭只記錄了Head_Type 。包體最大長度預設為128位元組,通過chunkSize可改變包體最大長度,通常當一段AFM資料超過128位元組後,超過128的部分就放到了其他的RTMP封包中,包頭為一個位元組.
完整的RTMP包頭有12位元組,由下面5個部分組成 :
用途 | 大小(Byte) | 含義 |
Head_Type | 1 | 包頭 |
TIMER | 3 | 時間戳 |
AMFSize | 3 | 資料大小 |
AMFType | 1 | 資料型別 |
StreamID | 4 | 流ID |
一)、Head_Type - 包頭型別
Head_Type佔用RTMP包的第一個位元組,這個位元組裡面記錄了包的型別和包的ChannelID。Head_Type位元組的前兩個Bit決定了包頭的長度.它可以用掩碼0xC0進行"與"計算:
Head_Type的前兩個Bit和長度對應關係:
Bits | Header Length |
00 | 12 bytes |
01 | 8 bytes |
10 | 4 bytes |
11 | 1 byte |
Head_Type的後面6個Bit和StreamID決定了ChannelID。 StreamID和ChannelID對應關係:StreamID=(ChannelID-4)/5+1 參考red5
ChannelID |
用途 |
02 | Ping 和ByteRead通道 |
03 | Invoke通道 我們的connect() publish()和自字寫的NetConnection.Call() 資料都是在這個通道的 |
04 | Audio和Vidio通道 |
05 06 07 | 伺服器保留,經觀察FMS2用這些Channel也用來發送音訊或視訊資料 |
例如在rtmp包的資料中裡面,發現被插入了一個0xC2,這個就是一位元組的包頭,並且channelID=2.
二)、TiMMER - 時間戳
時間戳佔用RTMP包頭的第2、3、4 三個位元組。RTMP時間戳可分為絕對時間戳和相對時間戳,紀錄的是音視訊的時間資訊。相對時間戳指的是二個RTMP包之間的時間間隔,單位毫秒。而絕對時間戳指的是當前封包傳送的時刻,單位也是毫秒。對於音視訊的播放,時間戳非常關鍵,因為音視訊的播放同步是由時間戳來控制的,如果你的視訊出現卡頓,音視訊不同步,延時越來越大,很可能就是你的時間戳不準導致的。
fms對於同一個流,釋出(publish)的時間戳和播放(play)的時間戳是有區別的
publish時間戳,採用相對時間戳,時間戳值等於當前媒體包的絕對時間戳與上個媒體包的絕對時間戳之間的差距,也就是說音視訊時間戳在一個時間軸上面.單位毫秒。
play時間戳,也是相對時間戳,時間戳值等於當前媒體包的絕對時間戳與上個同類型媒體包的絕對時間戳之間的差距, 注意這裡跟上面不同的是強調“同類型的媒體包”。也就是說音視訊時間戳分別採用單獨的時間軸,單位毫秒。
flv格式檔案時間戳,絕對時間戳,時間戳長度3個位元組。超過0xFFFFFF後時間戳值等於TimeStamp & 0xFFFFFF。
flv格式檔案影片總時間長度儲存在onMetaData的duration屬性裡面,長度為8個位元組,是一個double型別。
三)、AMFSize - 資料大小
AMFSize佔三個位元組,這個長度是AMF長度,可超過RTMP包的最大長度128位元組。如果超過了128位元組,那麼由多個後續RTMP封包組合,每個後續RTMP封包的頭只佔一個位元組。一般就是以0xC?開頭。1個位元組的包頭表示這個包的時間戳、資料大小、資料型別、流ID都和上一個相同ChannelID的RTMP包完全一樣。
四)、AMFType - 資料型別
AMFType是RTMP包裡面的資料的型別,佔用1個位元組。例如音訊包的型別為8,視訊包的型別為9。下面列出的是常用的資料型別:
0×01 | Chunk Size | changes the chunk size for packets |
0×02 | Unknown | |
0×03 | Bytes Read | send every x bytes read by both sides |
0×04 | Ping | ping is a stream control message, has subtypes |
0×05 | Server BW | the servers downstream bw |
0×06 | Client BW | the clients upstream bw |
0×07 | Unknown | |
0×08 | Audio Data | packet containing audio |
0×09 | Video Data | packet containing video data |
0x0A-0x0E | Unknown | |
0x0F | FLEX_STREAM_SEND | TYPE_FLEX_STREAM_SEND |
0x10 | FLEX_SHARED_OBJECT | TYPE_FLEX_SHARED_OBJECT |
0x11 | FLEX_MESSAGE | TYPE_FLEX_MESSAGE |
0×12 | Notify | an invoke which does not expect a reply |
0×13 | Shared Object | has subtypes |
0×14 | Invoke | like remoting call, used for stream actions too. |
0×16 | StreamData | 這是FMS3出來後新增的資料型別,這種型別資料中包含AudioData和VideoData |
五)、StreamID - 流ID
佔用RTMP包頭的最後4個位元組,是一個big-endian的int型資料。我們x86 計算機記憶體中資料存放都是小尾數模式:little-endian,而網路資料流一般都是大尾數模式:big-endian。 StreamID是音視訊流的唯一ID, 一路流如果既有音訊包又有視訊包,那麼這路流音訊包的StreamID和他視訊包的StreamID相同,但ChannelID不同。
ChannelID 和StreamID之間的計算公式:StreamID=(ChannelID-4)/5+1 參考red5。如果這個封包既不是音訊包,也不是視訊包,那麼他的StreamID=0.
例如當音視訊包ChannelID為2、3、4時StreamID都為1 當音視訊包ChannelID為9的時候StreamID為2
六)、封包分析
例如有一個RTMP封包的資料0300 00 00 00 01 02 1400 00 00 00 0200 07 63 6F 6E 6E 65 63 74 003F F0 00 00 00 00 00 00 08 ,,,
資料依次解析的含義
03表示12位元組頭,channelid=3
000000表示時間戳 Timer=0
000102表示AMFSize=18
14表示AMFType=Invoke 方法呼叫
00 00 00 00 表示StreamID = 0
//到此,12位元組RTMP頭結束下面的是AMF資料分析,具體的AMF0資料格式請參考 RTMP協議 二、AMF資料
02表示String
0007表示String長度7
63 6F 6E 6E 65 63 74 是String的Ascall值"connect"
00表示Double
3F F0 00 00 00 00 00 00 表示double的0.0
08表示Map資料開始
二、AMF資料
Rtmp包預設的最大長度為128位元組,(或通過chunksize改變rtmp包最大長度), 當AMF資料超過128Byte的時候就可能有多個rtmp包組成,如果需要解碼的rtmp包太長則被TCP協議分割成多個TCP包.那麼解碼的時候需要先將包含rtmp包的tcp封包合併, 再把合併的資料解碼,解碼後可得到amf格式的資料,將這些AMF資料取出來就可以對AMF資料解碼了.
RTMP封包包括包頭和AMF資料2部分,AMF資料裡面可以是命令也可以是音視訊資料。組成伺服器和Flash客戶端之間的所有資料都是用AMF格式的資料在傳送,例如connect() publish()等命令. AMF資料由2部分組成: ObjType 加上 ObjValue。ObjType的大小為一個位元組。ObjValue的大小不固定,和ObjType相關。 常用的ObjType型別和對應的ObjValue大小整理如下,詳細的ObjType的資料在本文的最下面列出:
型別說明(ObjType) | 資料 | dataSize |
CORE_String | 0x02 | 2位元組 (2位元組的資料紀錄了String的實際長度) |
CORE_Object | 0x03 | 0位元組(開始巢狀0x00000009表示巢狀結束) |
NULL | 0x05 | 0位元組 空位元組無意義 |
CORE_NUMBER | 0x00 | 8位元組 |
CORE_Map | 0x08 | 4位元組(開始巢狀) |
CORE_BOOLEAN | 0x01 | 1位元組 |
ObjValue不一定是一個固定的大小,他可以包含另外一個AMF資料,這另外一個AMF資料裡面又有ObjType 加上 ObjValue,也就是AMF資料的巢狀關係
AMF0資料的巢狀關係如下:
Object={ObjType + ObjValue}
CORE_BOOLEAN={Value(1 Byte)}
CORE_NUMBER={Value(8 Byte)}
CORE_String={StringLen(2 Byte) + StringValue(StringLen Byte)}
CORE_DATE={value(10 Byte)}
CORE_Array={ArrayLen(4 Byte) + Object}
CORE_Map={MapNum(4 Byte) + CORE_Object}
CORE_Object={CORE_String + Object}
看起來有些複雜,所以我這裡圖文並茂來詳解,例如完成握手後,Flash向FMS傳送的第一個RTMP資料,內容如下:
藍色的表示包頭,紅色的表示ObjType,綠色的表示資料。上面一段資料由2個RTMP包組成,2個RTMP包頭分別用藍色表示,第一個藍色的是12位元組的包頭,後面一個藍色的C3是一個位元組的包頭,綠色部分是資料,紅色的是AMF資料型別,整個RTMP解碼過程如下
[2008-06-18 16:59:20] DecodeInvoke:
[2008-06-18 16:59:20] InvokeName:String:connect
[2008-06-18 16:59:20] InvokeID:Double:0
[2008-06-18 16:59:20] Map:MapNum:0
[2008-06-18 16:59:20] Params:{
[2008-06-18 16:59:20] Key:String:objectEncoding
[2008-06-18 16:59:20] Value:Double:0
[2008-06-18 16:59:20] Key:String:app
[2008-06-18 16:59:20] Value:String:mediaserver
[2008-06-18 16:59:20] Key:String:fpda
[2008-06-18 16:59:20] Value:Bool:0
[2008-06-18 16:59:20] Key:String:tcUrl
[2008-06-18 16:59:20] Value:String:rtmp://127.0.0.1/mediaserver
[2008-06-18 16:59:20] Key:String:audioCodecs
[2008-06-18 16:59:20] Value:Double:615
[2008-06-18 16:59:20] Key:String:videoCodecs
[2008-06-18 16:59:20] Value:Double:76
[2008-06-18 16:59:20] }End Params
[2008-06-18 16:59:20] InvokeParams:String:PUBLISHER
[2008-06-18 16:59:20] InvokeParams:String:streamRecode
FMS3中為了實現H.264資料的直播而增加了一個數據型別,這個型別的值為0x16,這個型別
關於rtmp封包中資料型別為0x16的封包
使用rtmp協議從FMS3中拉音視訊資料的時候,會收到AMFType=0x16的封包,這種包在FMS2中從沒有出現過.
rtmp包頭的第8個位元組就是AMFType,也就是資料型別。例如AMFType=0x08表示音訊包,AMFType=0x04表示Ping包等等。FMS3中為了實現H.264資料的直播而增加了一個數據型別,這個型別的值為0x16。AMFType=0x16的包中既包含了音訊幀也包含了視訊幀。其中音訊幀和視訊幀是一種新的格式存放的,類似FLV檔案儲存格式,每個音視訊包作為一個Tag,許多的Tag組成了這個AMFType=0x16的資料型別,Tag的格式如下:
用途 | 大小(Byte) | 資料含義 |
StreamType | 1 | 流的種類(0x08=音訊,0x09=視訊) |
MediaSize | 3 | 媒體資料區域大小 |
TiMMER | 3 | 絕對時間戳,單位毫秒 |
Reserve | 4 | 保留,值為0 |
MediaData | MediaSize | 媒體資料,音訊或視訊 |
TagLen | 4 | 幀的大小,值為媒體資料區域大小+引數長度(MediaSize+1+3+3+4) |
詳細的ObjType資料型別如下:,參考Red5
enum AMF
{
/**
* Boolean value marker constant
*/
TYPE_BOOLEAN = 0x01,
/**
* String marker constant
*/
TYPE_STRING = 0x02,
/**
* Object marker constant
*/
TYPE_OBJECT = 0x03,
/**
* Movieclip marker constant
*/
TYPE_MOVIECLIP = 0x04 ,
/**
* Null marker constant
*/
TYPE_NULL = 0x05,
/**
* Undefined marker constant
*/
TYPE_UNDEFINED = 0x06,
/**
* Object reference marker constant
*/
TYPE_REFERENCE = 0x07,
/**
* Mixed array marker constant
*/
TYPE_MIXED_ARRAY = 0x08,
/**
* End of object marker constant
*/
TYPE_END_OF_OBJECT = 0x09,
/**
* Array marker constant
*/
TYPE_ARRAY = 0x0A,
/**
* Date marker constant
*/
TYPE_DATE = 0x0B,
/**
* Long string marker constant
*/
TYPE_LONG_STRING = 0x0C,
/**
* Unsupported type marker constant
*/
TYPE_UNSUPPORTED = 0x0D,
/**
* Recordset marker constant
*/
TYPE_RECORDSET = 0x0E,
/**
* XML marker constant
*/
TYPE_XML = 0x0F,
/**
* Class marker constant
*/
TYPE_CLASS_OBJECT = 0x10,
/**
* Object marker constant (for AMF3)
*/
TYPE_AMF3_OBJECT = 0x11,
/**
* true marker constant
*/
VALUE_TRUE = 0x01,
/**
* false marker constant
*/
VALUE_FALSE = 0x00
};