rtmp官方協議詳解

標準規範學習:

rtmp消息結構,包括幾個部分:
時戳:4  byte,單位毫秒。超過最大值後會翻轉。
長度:消息負載的長度。
類型ID:Type Id 一部分ID範圍用於rtmp的控制信令。還有一部分可以供上層使用,rtmp只是透傳。這樣可以方便的在rtmp上進行擴展。
消息流ID:Message Stream ID,用於區分不同流的消息。
 
兩個ID的區別:
Message stream:傳輸消息的邏輯通道。
Message stream ID:每個消息都有一個流id,用於指明屬於哪個流。
 
Chunk:是更底層的一個概念。Message有可能過大,需要分割成一個個碎片,chunk就是一個消息的部分碎片。
Chunk stream:傳輸chunk的邏輯通道。
Chunk stream ID:用於標示chunk屬於哪個邏輯通道。
message stream和chunk stream應該屬於不同的層次,message屬於應用層次消息,chunk屬於更底層rtmp協議層次。
一個chunk stream 上能夠跑多個message stream。message stream id在一個chunk stream下要不相等。
 
消息會切分成一個個小的塊進行傳輸。
 
rtmp的時戳單位是毫秒。
連接握手流程圖:
握手過程:服務器和客戶端各自發送三個包:c0,c1,c2,s0,s1,s2,服務器必須在收到c0後才發送s0和s1,也可以等到c1才發送;服務端必須收到c1才能夠發送s2,客戶端必須收到s2才能夠發送c2
 
c0和s0就是一個字節:表示協議版本號。現在是03。
01 234567
+-+-+-+-+-+-+-+-+
|    version    |
+-+-+-+-+-+-+-+-+
  C0 and S0 bits
 
c1和s1長度1536,
抓包發現,zero字段不一定爲0。
 
c2和s2的長度也是1536
通過vlc和nginx rtmp抓的包發現和規範不對應。安裝規範,time和time2應該是爲了計算帶寬和延遲。
 
 
交互過程:
一般客戶端一起發生c0和c1,然後服務器直接發送s0,1,2。客戶端收到後發送c2,握手完成。三次交互就可以了。
 
塊流:

一個連接可以穿輸多個塊流。每個塊流有不同的id來區分。
塊傳輸運行將大的消息包切割爲小的消息包。防止大的數據比如視頻阻塞連接,導致音頻和信令也無法傳遞。
小的消息也可以成本更加低的傳輸,包頭會壓縮.
 
塊流大小可以配置。有一個消息:set chunk size可以協商大小。大的塊可以降低cpu 負載,但是可能會阻塞重要消息;小的塊不利於傳輸,特別是在低帶寬的情況下。
 
塊允許 層協議將大的消息 解 更小的消息,例如,防 體積大的但優先級小的消 息 (比如視頻) 阻礙體積較小但優先級高的消息 (比如音頻或者控制命 )——傳送過程,音頻和控制的優先級較高。
 
塊的大小是  配置的 它  使用一個設置塊大小的控制消息 行設置 (參考 5.4.1) 更大的塊大小  降  CPU 開銷,但在  寬 接時因 它的大量的寫入也會延   他內容的傳遞 更小的塊不利於高比特率的流化 所 塊的大小設置 決於 體情況。——這是使用tcp必須要考慮的問題。塊過大會影響音頻和控制的發送。過小會影響效率和比特率。
 
塊類型:
 
塊基本頭:
 

 
 
第一個字節都是這樣。其中fmt用來指示Message Header。
cs id:
cs id是chunk stream id的縮寫。範圍是3——65599。可變字節,後面可以跟一個字節,也可以跟兩個字節。跟幾個字節是有第一個字節的後面6位的值決定的。這一點中文翻譯寫的不完整。
如果最後6位值爲0,則表示後面只有一個字節。id範圍是64——319,其實就是0——255,最大和最小加64。
如果最後6位值爲1,則表示後面只有兩個字節。id範圍是64 - 65599,其實就是0——65535,最大和最小加64。j
計算方法比較特殊:(第三 個 節) * 256 + 第二 個 節 + 64
 
塊流id應該是區分每一個流,比如,一個視頻通話,包括視頻和音頻兩個流。這裏的意思應該是一個連接可以傳遞多個流,通過塊流id進行區分。
 
塊消息頭

塊消息頭格式由塊基本頭的fmt字段決定。共四種。
 
fmt:
四種類型:0 1 2 3
包頭必須儘可能的壓縮。
 
type 0:用在流開始,或者時戳重置的時候。message header消息頭共11byte。
三字節時戳:表示範圍爲0——16777215(0xffffff),如果大於等於16777215,則表示還有一個字節的擴展時戳。chunk header的第三部分就是extend timestamp。這一本部分是否存在由三個字節的值決定。注意,這個是絕對時戳,而後面的幾種類型trunk都是時戳的增量;區分消息截止是根據消息的長度來進行的。那麼輸入處理主動丟棄包的情況?
 
 
 
type 1:message header有7byte,比type 0少了4個字節的message stream id,使用上一個塊的id。大小可變的消息(比如視頻的每一幀數據)往往會在第一幀(第一幀的第一個chunk需要用type0)後面的幀的第一個chunk中使用type1。爲什麼?因爲可變數據,必須有長度信息記錄。
 
type2:只有三個字節。沒有message stream id和message 長度。全部使用同一個chunk stream的上一個chunk的。適用於長度不變的步伐傳輸。
 
type 3:0個字節的message header。時戳,長度,消息流id全部使用同一個chunk stream的上一個chunk。當一個消息切分後劃分到多個chunk的時候,除了帶個chunk,所有的其他chunk應該使用type3。——如何判斷包結束?根據消息的長度,第一個chunk中的長度應該是一個消息完成的長度。
 
四種格式的使用:
第一個消息的第一個chunk 使用type0,攜帶全部的信息;後面的chunk使用type3;
第二個消息,如果是視頻消息,長度可變,則第一個chunk使用type1,值丟棄message stream id即可。第二個消息的其他chunk使用type3。
 
如果是音頻,且長度相同,則第一個消息用type0+type3,第二個消息用type2+type3。
如果音頻消息,長度,消息間時戳增量(delta)都相同,則第一個消息可以用type0+type3,後面的所有消息全部用type3即可。這樣的話也要求第一個消息的時戳必須和消息間的時戳增量相同。
 
 
注意:除type0外,其他的全部是時戳的增量。而不是時戳絕對值。
從上面來看,音頻和視頻的chunk id應該是不一樣的。否則無法區分。因爲有些塊是沒有message stream id,只有chunk stream id的。
 
公共字段定義:
1、timestamp delta:表示的是上一個chunk 和當前chunk的時戳差。不是消息的時間差。如果大於等於16777215,則表示還有一個字節的擴展時戳。chunk header的第三部分就是extend timestamp。
2、消息長度:消息的長度。他和chunk的payload的大小是不同的。chunk size length是所有chunk的大小再加最後一個大小。——消息長度是消息實際的大小。。
3、message type id:標示消息類型,比如音頻,視頻,控制等。
4、message stream id:以小端序存儲。
Extended Timestamp:當時戳大於
 
play2:可以實現比特率的切換。
 
message length,chunk size,tcp分包和消息的切割

終於弄明白其中的原理了。本來這幾個概念和方法在規範中描述不是很清楚,結合抓包終於弄清楚 了。
message length:及時消息本身的大小。這個消息要在chunk中進行傳輸。
chunk size:chunk的大小。chunk的大小默認是128。可以通過消息設置chunk size 的最大值。每個chunk的大小最大不能夠超過max chunk size。
明白上面兩點後進行分析:
1、如果message length小於等於max chunk size,則一個chunk中就包含着一個message。
2、如果message length大於max chunk size,則需要將這個message切分爲多個chunk。前面幾個chunk size必須是max 
size,最後一個就是剩餘的大小。
 
tcp分包過程:
1、收到chunk,明確chunk stream id,對一個chunk stream的數據進行處理。
2、判斷fmt類型,確定消息長度。
3、如果長度小於等於max chunk size,則這個chunk body的大小就是message length。
4、處理完這個chunk,跳過chunk header 和chunk body,就是下一個chunk。
5、如果長度大於max chunk size,則chunk body的大小就是max chunk size。處理這一部分數據,然後跳過max chunk size找到下一個chunk的頭。有消息長度減去max chunk size計算剩餘的數據長度,判斷剩餘長度是否大於max chunk size,如果大於,則表明這個chunk 大小還是max chunk size。以此類推,直到最後一個長度小於等於max chunk size,那麼這個長度就是這個chunk的實際長度,並且是這個消息的最後一個消息切片。然後把所有的有效切片拼接起來,就是完整的消息。後面收到的將是另外一個消息。
 
協議控制消息:

RTMP使用1,2,3,4,5,6的message type id作爲控制消息。控制協議的message stream id必須是0, chunk stream id必須是2(2的作用。0,1用來表示字節個數,2是信令,3-6xxxx是實際的。)。控制信令一收到就生效——就是沒有協商過程。。。——時戳可以忽略。
 
set chunk size (1):設置chunk的最大size

message type id 爲1。共四個字節:
第一位必須爲0。
 
默認值是128,服務端和客戶端都可以設置chunk size。注意,一個交互中chunk size是相互獨立的,也就是,客戶端可以設置他發送過去的chunk size,服務端也可以設置他發送過去的chunk size。兩個是沒有影響的。
chunk size最小建議128,必須大於等於1。
chunk size的範圍是1到0x7fffffff(2147483647),但是因爲message header中message length只有三個字節,所以chunk size的最大值是16777215  如果大於此,就去他。
 
Abort Message (2)

message type id爲2,四個字節,攜帶的內容是chunk stream id。用於通知對端,如果這個chunk stream還有消息正在等待接收未到達的消息內容,則停止,並丟棄原先接受的內容。可以用在帶寬有限是優先發送音頻和信令,丟棄視頻。
 
Acknowledgement (3) Window Acknowledgement Size (5)

Window Acknowledgement Size用於設置窗口確認大小,Acknowledgement是窗口確認消息。
會話開始時,雙方都要先對端發送Window Acknowledgement Size,用於指明期望獲得確認的大小。當一端收到內容大小超過Window Acknowledgement Size,就要像對方發送Acknowledgement。
1、會話開始計算收到byte個數的時間點是收到Window Acknowledgement Size消息開始。
2、byte size不包括tcp包頭,應該是chunk的大小,即從tcp 的recv函數中獲得的內容大小。
3、雙方都要向對方發送Window Acknowledgement Size和Acknowledgement。
4、發送端發送完Window Acknowledgement Size消息後,沒有收到Acknowledgement是不再發送進一步的消息的——這樣會容易引起錯誤,導致再也發送不出消息了。
 
 
Set Peer Bandwidth (6)

 
設置對端輸出帶寬。對端是通過設置Window Acknowledgement Size來實現流量控制的。超過Window Acknowledgement Size後未確認發送端將不再發送消息。所以對端收到set peer bandwidth後,如果之前發送的Window Acknowledgement Size和這裏寫的的Window Acknowledgement Size不一樣,一般會發送一個Window Acknowledgement Size。
 
 
message format

 
上面的協議控制消息的type id是1,2,3,5,6(這些是chunk stream的控制),沒有4。4是另外一個類型:
User Control Messages (4):用戶控制協議,是RTMP stream的控制協議。同樣,chunk stream id要設置爲2,message stream id要設置爲0。
 
消息體格式:兩個字節的event type,後面跟可變長度的event data,
 
RTMP Command Messages

客戶端和服務器之間可以發送的消息包括:音頻消息,視頻消息,數據消息,命令消息。命令消息採用AMF編碼。
 
command message type 20,17表示是命令消息。20表示使用AMF0編碼,17表示使用AMF3編碼。
命令消息包括connect, createStream, publish, play, pause ,響應消息使用onstatus,result。
 
Data Message (18, 15):數據消息,用於傳輸Metadata或用戶數據。18表示AMF0,15是AMF3。
 
Shared Object Message (19, 16):19表示AMF0,16是AMF3。
 
Audio Message (8):音頻消息
Video Message (9):視頻消息
 
Aggregate Message (22):一個消息中包含多個消息。
 
Types of Commands( 20,17)

netConnetion 命令:
connect :請求連接到服務器的應用實例。
 
call:請求一個遠程調用。
 
creatStream:創建一個邏輯通道,用於客戶端來發送音頻,視頻,描述數據。netConnection是默認通道,message stream id是0,creatStream使用0作爲message stream id。
 
NetStream Commands:
多個Netstream可以共用一個netconnection。但是stream的id是在什麼時候確定的???——疑問
 
play2:切換到不同的比特率的流上,而不用更改播放時間。這是一個有用的功能。這個消息是在paly之後再發送。
 
deleteStream:刪除流。
 
receiveAudio:是否接受音頻。如果爲false,服務器不用回覆。如果爲true,服務器回覆兩個狀態:
NetStream.Seek.Notify and   NetStream.Play.Start
 
receiveVideo:同樣。
 
publish:發佈一個流到server。需要onStatus響應。
 
seek:偏移。毫秒爲單位。
 
pause:暫停。
 
example:
首先,connect是連接的服務器端的應用(nginx rtmp有配置多個應用。)
 
視頻裸數據是如何打包進rtmp包的?

ffmpeg在flv和h264文件將的轉換是有問題的。
 
視頻性格的數據包格式和flv的格式非常的像,基本上就是flv格式的變種。參考flv文件格式官方協議詳解。
視頻相關包包括:onMetaData,AVCDecoderConfigurationRecord,h264數據。和flv文件中的區別是flv文件用tag頭,rtmp中相關信息是放在rtmp頭中。至於內部內容是一樣的。
 
 
AMF0編碼格式

  1. *AMF數據類型: 
  2.  *Type      Byte code 
  3.  *Number    0x00 
  4.  *Boolean   0x01 
  5.  *String    0x02 
  6.  *Object    0x03 
  7.  *MovieClip 0x04 
  8.  *Null      0x05 
  9.  *Undefined 0x06 
  10.  *Reference 0x07 
  11.  *MixedArray    0x08 
  12.  *EndOfObject   0x09 
  13.  *Array         0x0a 
  14.  *Date          0x0b 
  15.  *LongString    0x0c 
  16.  *Unsupported   0x0d 
  17.  *Recordset     0x0e 
  18.  *XML           0x0f 
  19.  *TypedObject (Class instance)  0x10 
  20.  *AMF3 data 0×11 
 
amf編碼:對個數據的合集,數據的邊界通過不同的類型,以及不同類型的長度來區分。
第一個字節是數據類型,然後根據數據類型確定數據的長度。其中,string是可變長度,兩個字節長度值。
如果是object,則object內部是個字典,key是字符串,value是數據,這裏還要對數據根據類型對數據解析,同上。
 
 

我 的微信公衆號



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章