从流程上对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:
總結
以上是生活随笔為你收集整理的从流程上对rtmp协议经行总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nginx架构详解(50%)
- 下一篇: nginx无法加载css