前言
網上好像沒一篇講的很完善的,可能和公司保密有關吧。先就最讓人困惑(至少我是這樣)且網上也很少找到答案的一個點講一下id各是什么意思? (如果我哪里理解錯了,希望大神指出,畢竟我也是看了好多資料及官方文檔總結的)
chunk stream id (cs id) 屬於Chunk Basic Header 占6bits
message type id 屬於Chunk Message Header 占1byte
message stream id 屬於Chunk Message Header 占4bytes
我們可以逆向推理:
接收端(一個chunk stream鏈接)收到的都是chunk 那么會根據chunk stream id來組合成不同的message,相同的chunk stream id當然就屬於同一message咯 但是會有不同的message,比如音頻、視頻、命令消息等,那么會根據message type id來區分是哪一類消息,比如我們通過message type id知道了是視頻消息,這時可能存在一種可能,對方同時發送了視頻a和視頻b(雖然這種情況很少,而且按照官方文檔的原話:this defeats the benefits of the header compression),那么就會根據message stream id來進一步區分了。
下面部分是我綜合了網上幾個寫的比較好的文章而來的,但是我沒保留原文網址,如果原作者反對,我會立即刪除。
rtmp介紹
RTMP 是Real-Time Messaging Protocol(實時消息傳送協議)的縮寫,它是Adobe Systems公司為Flash播放器和服務器之間音頻、視頻和數據傳輸開發的協議。這是一個標准的,未加密的實時消息傳遞協議,默認端口是1935。
rtmp協議分析
參考官方文檔:rtmp_specification_1.0.pdf(不能完全相信該文檔,adobe的這份文檔公認的差)
在RTMP協議中信令和媒體數據都稱之為Message,在網絡中傳輸這些Message,為了區分它們肯定是要加一個Message head的,所以RTMP協議也有一個Message head,還有一個問題因為RTMP協議是基於TCP的,由於TCP的包長度是有限制的(一般來說不超過1500個字節),而RTMP的Message長度是有可能很大的,像一個視頻幀的包可能會有幾十甚至幾千K,這個問題就必然有一個分片的問題,在RTMP協議中對應的說法就是chunk,每一個Message + head都是由一個和多個chunk組成的。
一個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
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
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字節的。
rtmp負載數據格式分析
信令格式
Rtmp信令格式是amf格式(官方文檔:amf3_spec_121207.pdf
)。簡單介紹下:
AMF是Action Message Format的簡寫,它是一種二進制的數據格式, 它的設計,是為了把actionscript里面的數據(包括Object, Array, Boolean, Number等)序列化成 一段你基本看不大懂的二進制數據, 然后你可以把這段數據隨意發送給其他地方的程序,比如發給遠程的服務器, 在遠程服務器那邊, 又可以把這段數據給還原出來。以此達到一個數據傳輸的作用。Amf有amf0和amf3兩個版本。AMF3消息流基本上都是在AMF3外面包了一層AMF0, 也就是說我們看到的所有AMF數據流都是AMF0的。
一段AMF消息流解析如下:
00 03 表示版本號為3, 其實個人覺得這個版本號用處不大,只是可以提醒你數據流中可能會遇到AMF3的數據
00 00 表示頭部的個數為0, 一般情況下貌似頭部個數都是0,我還沒理解什么情況下要用到頭部,如果頭部個數為n,那么接下來應該是n個頭部的數據,這里因為n=0,所以直接跳過
00 01 表示消息體的個數為1
接下來就是(消息體的正文=targetURI+responseURI+內容長度+內容)* n,我們這個例子里n=1
targetURI是一個字符串,它表示這個消息要發到哪里去,在這個例子里它 = amfService.dispatchAMF,responseURI也是一個字符串,它其實就類似於一個token的作用,因為我們同時可能調用了很多個service,那么service返回回來的數據要回調哪個處理函數呢?關鍵就在於這個responseURI,這個字符串會跟着數據流發出去, 然后還會回來。AMF0規定了targetURI和responseURI都是一個UTF字符串, 也就是用2個字節來表示字符串長度,后面緊接着字符串正文。
繼續往下看
00 16 表示targetURI的長度是0×16,
61 6D……41 4D 46 就是targetURI的值: amfService.dispatchAMF
00 03 表示responseURI的長度是3
2F 36 34 表示responseURI的值: /64
00 00 00 4D 表示后面的內容長度為0x4D
接下來的都是內容正文了,內容正文的數據全是AMF0的的數據
AMF的數據都是一個字節表示type(文檔中稱之為maker),然后緊接着數據
0A 表示這個是一個標准的Array
00 00 00 02 表示數組有2個元素
02 表示第一個元素的type是字符串,
00 30 表示第一個元素的字符串的長度是0×30
32 … 33 37 表示的是字符串的值
00 表示第2個元素是一個Number
40 00 00 00 00 00 00 00 表示的就是這個Number的值
上面這個例子正好不含有AMF3的數據,說明了開頭的那個版本號3其實並沒有太大意義,我們現在看到的數據都是AMF0的。
媒體流格式
Rtmp媒體流格式可以看成是flv里面video tag格式里面的data的格式。(官方文檔:video_file_format_spec_v10_flv_f4v.pdf)。簡單介紹下:
第1個字節包含視頻數據的參數信息
第1個字節的前4位的數值表示幀類型
第1個字節的后4位的數值表示視頻編碼ID,1 = JPEG(現已不用),2 = Sorenson H.263,3 = Screen video,4 = On2 VP6,5 = On2 VP6 with alpha channel,6 = Screen video version 2。
從第2個字節開始為視頻流數據。格式為AVCVIDEOPACKET
第一個字節為AVCPacketType, 0: AVC sequence header 1: AVC NALU 2: AVC end of sequence
第2,3,4個字節為CompositionTime 一般為0即可
接下來的就是數據部分了。
rtmp服務的實現
由於我們的項目主要是放在設備上的,對項目的大小及語言有要求,因此就不能直接 一些開源的項目,現在是基於rtmpdump里面的一個rtmpsvr.c實現的rtmp服務端。Rtmpdump是一個rtmp客戶端, rtmpsvr.c是rtmpdump里面附帶的用於它自己測試的。
主要流程包括:
1、HandShake
需要注意的是:RTMP Specifiction 1.0文檔已經過時,從大概Adobe Flash Player 10.0.32.18版本開始,HandShake已不按該文檔進行,文檔中MUST be all 0的字節被填上了數據。當HandShake數據是這樣時,要求回應的結尾有特定的數據,否則Adobe Flash Player將關閉H264/AAC的解碼功能。Rtmpdump里面有實現握手,具體實現可以參考它
2、信令交互
交互的信令如下圖所示:信令的格式參考 第三節的信令格式。
3、流數據傳輸
開始是流相關的信令交互,然后就是流數據了,如下圖所示:
流的格式參考 第三節的媒體流格式。
一些RTMP項目,有Server端也有Client端
· Red5 only contains a server-implementation (in java).
· The python project rtmpy aims to be a free
software implementation of an RTMP library, whilst Tape intends to be a full
streaming server (in Python). rtmpy is in active development.
· There is a java client implementation of RTMP, called Flazr.
As of 2nd June 2009 (just two weeks after Adobe issued the take-down notice),
Flazr also has RTMPE support.
Congratulations to Peter, and thank you to Adobe: none of this would have
remotely happened, if you hadn't brought RTMPE to people's attention.
· SWFDechas a partial and experimental implementation of RTMP.
swfdec is client-only.
· Gnashhas a partial and experimental implementations of RTMP.
Gnash has both client and server, sharing the same common source. Cygnal
is making particularly good progress, as a server: video can already be
streamed from it, with real-time video planned for Q3 2009.
· libRTMPby boxee contains an RTMP client library, and was used as
the basis for rtmpdump.
· haxeVideois a server implementation of RTMP in Haxe.
· crtmpserveris a server implementation of RTMP that has implemented
(as of 25th may 2009) the RTMPE protocol.
· Mammoth, formerly known as OpenFMS, is a server implementation
that has implemented an RTMPE-compatible algorithm known as
"H264-compatible and DH handshaking".
· RubyIzumiis an implementation of an RTMP server in Ruby.
· rtmpdump此項目是第一個突破RTMPE加密協議的,但因為破解了RTMPE所以惹惱了ADOBE,這個開源項目在SourceForge里已經沒有了。但是作者的頁面還保留有鏈接和代碼。
· http://rtmpd.com/ 這個項目極其龐大,還沒有一些深入的了解
flvstreamer http://savannah.nongnu.org/projects/flvstreamer/
Wowza http://www.wowza.com/lp2?network=d&ad=15567605046&keyword=media+server&matchtype=&placement=thompsonng.blogspot.com&experiment=&mobile=&gclid=CN21xYDU2rQCFaN_Qgod9RQAXw
完
2013年1月