分析的基本清晰。
原文地址 從流程上對rtmp協議經行總結
拷貝黏貼
一、握手:
1、C0:C—>S
2、S0: S—>C
名稱:C0 S0
長度:1字節
對於版本號的定義:當前rtmp協議的版本號一致爲“3”,0、1、2是舊版本號,已經棄用。4-31被保留爲rtmp協議的未來實現版本使用;32-255不允許使用。如果服務器端或者客戶端收到的C0字段解析出爲非03,如果是0x06考慮使用openssl進行解密C1 C2 S1 S2,如果對端不支持加密字段可以選擇以版本3來響應,也可以放棄握手。
簡單握手:
作用:C0和S0一致,都是一個字節,都代表當前使用的rtmp協議的版本號。如果服務器端或者客戶端收到的C0/S0字段解析出爲非03,對端可以選擇以版本3來響應,也可以放棄握手。
複雜握手:
作用:說明是明文還是密文。如果使用的是明文(0X03),同時代表當前使用的rtmp協議的版本號。如果是密文,該位爲0x06
3、C1: C—>S
4、S1: S—>C
名稱:C1 & S1
長度:1536字節
簡單握手:
作用:
包結構:
time(4字節)+zero(4字節)+ random data(1528字節)。
Time(4字節):這個字段包含一個timestamp,用於本終端發送的所有後續塊的時間起點。這個值可以是0,或者一些任意值。要同步多個塊流,終端可以發送其他塊流當前的timestamp的值,以此讓當前流跟要同步的流保持時間上的同步。
Zero (4個字節):這個字段必須都是0。如果不是0,代表要使用complex handshack。
Random data (1528個字節):這個字段可以包含任意值。終端需要區分出響應來自它發起的握手還是對端發起的握手,這個數據應該發送一些足夠隨機的數。這個不需要對隨機數進行加密保護,也不需要動態值。
複雜握手:
作用:用於驗證服務器端或者client端的有效性。
包結構:
time(4字節)+version(4字節)+key(764字節)+digest(764字節)。總共1536字節
其中,key和digest可能會交換位置,也就如下圖有兩種格式:schemal0&schemal1。
客戶端決定使用哪種schema方式,服務器端比較倒黴,需要將兩種方式都嘗試,一般是先按照schema0解析,失敗則使用schema1解析。但是無論key和digest位置如何,它們的結構是不變的。
Time(4字節):這個字段包含一個timestamp,用於本終端發送的所有後續塊的時間起點。這個值可以是0,或者一些任意值。要同步多個塊流,終端可以發送其他塊流當前的timestamp的值,以此讓當前流跟要同步的流保持時間上的同步。
Version(4個字節):4bytes 爲程序版本。C1一般是0x80000702。S1是0x04050001。貌似這個可以隨意填寫,但是要採用非0值跟simple handshack區分。
Key(764個字節):
random-data:長度由這個字段的最後4個byte決定,即761-764
key-data:128個字節。Key字段對應C1和S1有不同的算法,這個需要注意。後面會詳細解釋。發送端(C1)中的Key應該是隨機的,接收端(S1)的key需要按照發送端的key去計算然後返回給發送端。
random-data:(764-offset-128-4)個字節
key_offset:4字節, 最後4字節定義了key的offset(相對於KeyBlock開頭而言,相當於第一個random_data的長度)
Digest(764個字節):
offset:4字節, 開頭4字節定義了digest的offset(相對於DigestBlock的第5字節而言,offset=3表示digestBlock[7~38]爲digest,【4-6】即爲第一個random_data)
random-data:長度由這個字段起始的4個byte決定
digest-data:32個字節。Digest字段對應C1和S1有不同的算法,這個需要注意。後面會詳細解釋。
random-data:(764-4-offset-32)個字節
算法:
C1的key爲128bytes隨機數。C1_32bytes_digest= HMACsha256(P1+P2, 1504, FPKey, 30) ,其中P1爲digest之前的部分,P2爲digest之後的部分,P1+P2是將這兩部分拷貝到新的數組,共1536-32長度。S1的key根據 C1的key算出來。
S1的digest算法同C1。注意,必須先計算S1的key,因爲key變化後digest也重新計算。
5、C2: c—>s
6、S2: s—>c
名稱:C2 & S2
長度:1536字節。
簡單握手:
作用:基本是C1&S1的副本
包結構:
time(4字節)+ Time2(4字節)+randomecho(1528字節)。
Time(4個字節):這個字段必須包含終端在S1 (給 C2) 或者 C1 (給 S2) 發的 timestamp。
Time2 (4個字節):這個字段必須包含終端先前發出數據包 (s1 或者 c1) timestamp。
Randomecho (1528個字節):這個字段必須包含終端發的 S1 (給 C2) 或者 S2 (給 C1) 的隨機數。兩端都可以一起使用 time 和 time2 字段再加當前 timestamp 以快速估算帶寬和/或者連接延遲,但這不太可能是有多大用處。
複雜握手:
作用:主要是用來提供對C1 S1的驗證
包結構:
randomdata(1504字節)+Digest-data(32字節)。
驗證算法:
分別拿到C1 和S1的數據,按照上文定義的計算方法再將C1或S1的digest字段計算一遍,跟當前從C1和S1中拿到的Digest字段進行比較
以上,關於rtmp協議握手的地方已經全部闡述完成。下面將分應用模式對各包進行解釋。
二、觀看流程:
7、Connect(‘stream名’):屬於命令消息類型(c—>s)
名稱:connect
作用:客戶端發送 connect 命令到服務器端來請求連接到一個服務器應用的實例
包結構:(結構跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
Command Name(字符串,命令的名字。設置給 "connect")+ Transaction ID(數字,總是設置爲 1)+ Command Object(對象,具有名值對(名:值)的命令信息對象)+ Optional User Arguments(對象,可選)+End of objectMarker(0x00 0x00 0x09)
包體中包含了很多“對象(object)”信息。具體內容請參照rtmp官方給出的協議。上面有詳細介紹。
8、Window Acknowledgement Size: 屬於協議控制消息(C—>S)
名稱:窗口確認大小。
作用:客戶端或者服務器端發送這條消息來通知對端發送和應答之間的窗口大小。發送者在發送完窗口大小字節之後期待對端的確認。接收端在上次確認發送後接收到的指示數值後,或者會話建立之後尚未發送確認,必須發送一個確認。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的爲5
RTMP_body:
WindowAcknowledgement Size(4byte)
9、Set peer bandwidth: 屬於協議控制消息(C—>S)(type id=6)
名稱:設置對端帶寬。
作用:客戶端或者服務器端發送這一消息來限制其對端的輸出帶寬。對端接收到這一消息後,將通過限制這一消息中窗口大小指出的已發送但未被答覆的數據的數量以限制其輸出帶寬。如果這個窗口大小不同於其發送給 (設置對端帶寬) 發送者的最後一條消息,那麼接收到這一消息的對端應該回復一個窗口確認大小消息。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的爲6
RTMP_body:
WindowAcknowledgement Size(4byte)+Limit type(1byte)
限制類型取以下值之一:
0 - Hard:對端應該限制其輸出帶寬到指示的窗口大小。
1 - Soft:對端應該限制其輸出帶寬到指示的窗口大小,或者已經有限制在其作用的話就取兩者之間的較小值。
2 - Dynamic:如果先前的限制類型爲Hard,處理這個消息就好像它被標記爲 Hard,否則的話忽略這個消息。
10、Stream begin 0:屬於用戶控制消息.(C—>S)
名稱:。
作用:服務器發送這個事件來通知客戶端一個流已就緒並可以用來通信。默認情況下,這一事件在成功接收到客戶端的應用連接命令之後以 ID 0 發送。這一事件數據爲 4 字節,代表了已就緒流的流 ID。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的爲4.csid是一定的爲2.stream id是一定的爲0。並且timestamp將被忽略。
RTMP_body:
Eventtype:stream begin(0)(2byte)+4byte的數據
11、_result(netconnect.connect.success):屬於命令消息類型(S-->C)
名稱:服務器應答命令。
作用:服務器端向客戶端發送的關於之前客戶端向服務器端請求的命令的響應結果。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
字段名 |
類型 |
描述 |
Command Name |
字符串 |
_result 或者 _error;表明回覆是一個結果還是錯誤。 |
Transaction ID |
數字 |
響應所屬的命令的 ID。 |
Command Object |
對象 |
如果存在一些命令信息要設置這個對象,否則置空。 |
Stream ID |
數字 |
返回值要麼是一個流 ID 要麼是一個錯誤信息對象。 |
12、onBWDone():屬於命令消息類型(S-->C)
名稱:
作用:服務器端向客戶端發送的關於之前客戶端向服務器端請求的命令的響應結果。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
13、Window acknowledgement size: 屬於協議控制消息(C—>S)具體參照上文。
14、creatStream(): 屬於協議控制消息(C—>S)
名稱:
作用:客戶端發送這一命令到服務器端以爲消息連接創建一個邏輯通道。音頻、視頻和元數據使用 createStream 命令創建的流通道傳輸。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
字段名 |
類型 |
描述 |
Command Name |
字符串 |
命令名。設置給 "createStream"。 |
Transaction ID |
數字 |
命令的事務 ID。 |
Command Object |
對象 |
如果存在一些命令信息要設置這個對象,否則置空。 |
15、set Buffer Length:屬於用戶控制消息.(C—>S)
名稱:窗口確認大小。
作用:客戶端發送這一事件來通知服務器端用於緩存流中任何數據的緩存大小 (以毫秒爲單位)。這一事件在服務器端開始處理流之前就發送。這一事件數據的前 4 個字節代表了流 ID 後 4 個字節代表了以毫秒爲單位的緩存的長度。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的爲4.csid是一定的爲2.stream id是一定的爲0。並且timestamp將被忽略。
RTMP_body:
Eventtype:Set Buffer Length(3)(2byte)+ 流ID(4byte) + 緩存的長度(以毫秒爲單位,4byte)
16、_result(netconnect.connect.success):屬於命令消息類型(S-->C)。具體內容參照上文
17、Play(‘app名’):屬於命令消息類型(c—>s)
名稱:play
作用:客戶端發送這一命令到服務器端以播放流。也可以多次使用這一命令以創建一個播放列表。
如果你想要創建一個動態的播放列表這一可以在不同的直播流或者錄製流之間進行切換播放的話,多次調用 play 方法,並在每次調用時設置Reset的值爲 false。相反的,如果你想要立即播放指定流,需要將其他等待播放的流清空,併爲將Reset設爲true。
包結構:(結構跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
字段名 |
類型 |
描述 |
Command Name |
字符串 |
命令名。設爲 "play"。 |
Transaction ID |
數字 |
事務 ID設爲 0。 |
Command Object |
Null |
命令信息不存在。設爲 null類型。 |
Stream Name |
字符串 |
要播放流的名字。要播放視頻 (FLV)文件,使用沒有文件擴展名的名字對流名進行定義 (例如,"sample")。要重播 MP3或者 ID3,你必須在流名前加上 mp3:例如,"mp3:sample"。要播放 H.264/AAC文件,你必須在流名前加上 mp4:並指定文件擴展名。例如,要播放 sample.m4v文件,定義"mp4:sample.m4v"。 |
Start |
數字 |
一個可選的參數,直播流交互過程中沒有此字段,可能是沒有添加。該字段以秒爲單位定義開始時間。默認值爲 -2,表示用戶首先嚐試播放流名字段中定義的直播流。如果那個名字的直播流沒有找到,它將播放同名的錄製流。如果沒有那個名字的錄製流,客戶端將等待一個新的那個名字的直播流,並當其有效時進行播放。如果你在 Start字段中傳遞-1,那麼就只播放流名中定義的那個名字的直播流。如果你在 Start字段中傳遞 0或一個整數,那麼將從 Start 字段定義的時間開始播放流名中定義的那個錄製流。如果沒有找到錄製流,那麼將播放播放列表中的下一項。 |
Duration |
數字 |
一個可選的參數,以秒爲單位定義了回放的持續時間。默認值爲 -1。-1值意味着一個直播流會一直播放直到它不再可用或者一個錄製流一直播放直到結束。如果你傳遞 0 值,它將只播放單一一幀,因爲播放時間已經在錄製流的開始的Start字段指定了。假定定義在 Start字段中的值大於或者等於0。如果你傳遞一個正數,將播放 Duration字段定義的一段直播流。之後,變爲可播放狀態,或者播放 Duration 字段定義的一段錄製流。(如果流在 Duration字段定義的時間段內結束,那麼流結束時回放結束)。如果你在 Duration字段中傳遞一個 -1以外的負數的話,它將把你給的值當做 -1處理。 |
Reset |
布爾 |
一個可選的布爾值或者數字定義了是否對以前的播放列表進行 flush。 |
18、set Buffer Length:屬於用戶控制消息.(C—>S),具體參照上文
19、Stream begin 1:屬於用戶控制消息.(C—>S),具體參照上文
20、onStatus(‘NetStream.Play.Reset’):屬於命令消息類型(S-->C)
名稱:
作用:服務器端使用"onStatus" 命令向客戶端發送 NetStream 狀態。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
字段名 |
類型 |
描述 |
Command Name |
字符串 |
命令名 "onStatus"。 |
Transaction ID |
數字 |
事務 ID 設置爲 0。 |
Command Object |
Null |
onStatus 消息沒有命令對象。 |
Info Object |
對象 |
一個 AMF 對象至少要有以下三個屬性。"level" (字符串):這一消息的等級,"warning"、"status"、"error" 中的某個值;"code" (字符串):消息碼,例如 "NetStream.Play.Start";"description" (字符串):關於這個消息人類可讀描述。 |
21、onStatus(‘NetStream.Play.Start’):屬於命令消息類型(S-->C),具體請參照上文
22、RtmpSampleAccess():屬於數據消息類型
名稱:
作用:客戶端或者服務器端通過發送這些消息以發送元數據或者任何用戶數據到對端。元數據包括數據 (音頻,視頻等等) 的詳細信息,比如創建時間,時長,主題等等。
包結構:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能爲0x12,代表包將採用AMF0方式進行編碼。也可以爲0x0E,此時表示包將採用AMF3方式進行編碼。stream id爲1.
RTMP_body:
23、onMetaData():屬於數據消息類型,同上文的RtmpSampleAccess
24、video data:type id=9=videodata
25、audio data:type id=8=audiodata
……
26、CloseStream():屬於命令消息類型
名稱:
作用:客戶端發送 closeStream 命令到服務器端來請求關閉連接通道
包結構:(結構跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能爲0x14,代表包將採用AMF0方式進行編碼。也可以爲0x11,此時表示包將採用AMF3方式進行編碼。
RTMP_body:
- 頂
- 0
- 踩