基本概念:
RTMP協(xié)議規(guī)定,播放一個流媒體有兩個前提步驟:
第一步,建立一個網(wǎng)絡(luò)連接(NetConnection)。
第二步,建立一個網(wǎng)絡(luò)流(NetStream)。
網(wǎng)絡(luò)連接代表服務(wù)器端應(yīng)用程序和客戶端之間基礎(chǔ)的連通關(guān)系,網(wǎng)絡(luò)流代表了發(fā)送多媒體數(shù)據(jù)的通道。服務(wù)器和客戶端之間只能建立一個網(wǎng)絡(luò)連接,但是基于該連接可以創(chuàng)建很多網(wǎng)絡(luò)流。
播放一個RTMP協(xié)議的流媒體需要經(jīng)過四個階段:
- 握手階段
- 建立連接階段
- 建立流階段
- 播放階段
RTMP連接都是以握手作為開始的,建立連接階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)連接”。
建立流階段用于建立客戶端與服務(wù)器之間的“網(wǎng)絡(luò)流”。
播放階段用于傳輸視音頻數(shù)據(jù)。
下面是使用librtmp執(zhí)行拉流過程的API調(diào)用流,如下:

抓包分析:
RTMP定義了較為完善的協(xié)議標準,但是每種播放工具的實現(xiàn)略有差異,下面是我使用VLC播放器拉流時抓取的報文,使用wireshark分析過程整理為下面的圖文。
先看一張總覽圖,圖中顯示的報文和時序包含了握手、建立連接、建立流和播放階段,如下:

還有申明下,以下的流程是根據(jù)實際抓包情況分析出來的,由于不同的工具省略了一些不必要的步驟,故不代表標準結(jié)果,僅供參考。
握手階段:
由于講解握手過程的文檔資料比較多,我這里就不重復描述了,摘圖如下:

個人認為這張圖是最符合標準時序的,細節(jié)拿捏得非常講究,雖然很多實現(xiàn)簡化了流程。
建立連接階段:
包括以下報文和步驟:
- 客戶端發(fā)起連接請求
- 服務(wù)器設(shè)置客戶端的應(yīng)答窗口大小
- 服務(wù)器設(shè)置客戶端的發(fā)送帶寬大小
- 服務(wù)器設(shè)置客戶端的接收塊大小
- 服務(wù)器響應(yīng)連接結(jié)果
- 客戶端設(shè)置服務(wù)器的應(yīng)答窗口大小
客戶端發(fā)起連接請求:
協(xié)議截圖如下:

協(xié)議方向:客戶端 -> 服務(wù)器
塊頭字段:
???? HeaderType: 0
???? CSID: 3
???? 時間戳:0
???? BodySize: 201
???? TypeID: 0x14
???? Stream ID: 0
負載格式:AMF0表示,connect 1 object1
???? object1屬性列表:
???? ???? "app": "live"
???? ???? "flashVer": "LNX 9,0,124,2"
???? ???? "tcUrl": "rtmp://127.0.0.1:1935/live"
???? ???? "fpad": false
???? ???? "capabilities": 15,
???? ???? "audioCodes": 4071,
???? ???? "videoCodes": 252,
???? ???? "videoFunction": 1,
???? ???? End Of Object Marker
服務(wù)器設(shè)置客戶端的應(yīng)答窗口大?。?/h6>
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 2
???? 時間戳:0
???? BodySize: 4
???? TypeID: 0x05
???? Stream ID: 0
負載格式:4字節(jié)整型表示,如5000000
服務(wù)器設(shè)置客戶端的發(fā)送帶寬大小:
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 2
???? 時間戳:0
???? BodySize: 5
???? TypeID: 0x06
???? Stream ID: 0
負載格式:5字節(jié)整型表示,前4字節(jié)為帶寬,后1字節(jié)為標志,如5000000, 2(動態(tài)調(diào)整)
服務(wù)器設(shè)置客戶端的接收塊大?。?/h6>
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 2
???? 時間戳:0
???? BodySize: 4
???? TypeID: 0x01
???? Stream ID: 0
負載格式:4字節(jié)整型表示,如4096
服務(wù)器響應(yīng)連接結(jié)果:
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 3
???? 時間戳:0
???? BodySize: 190
???? TypeID: 0x14
???? Stream ID: 0
負載格式:AMF0表示,_result 1 object1 object2
???? object1屬性列表:
???? ???? "fmsVer": "FMS/3,0,1,123"
???? ???? "capabilities": 31,
???? ???? End Of Object Marker
???? object2屬性列表:
???? ???? "level": "status"
???? ???? "code": "NetConnection.Connect.Success",
???? ???? "description": "Connection succeeded.",
???? ???? "objectEncoding": 0
???? ???? End Of Object Marker
客戶端設(shè)置服務(wù)器的應(yīng)答窗口大?。?/h6>
協(xié)議截圖如下:

協(xié)議方向:客戶端 -> 服務(wù)器
塊頭字段:
???? HeaderType: 0
???? CSID: 2
???? 時間戳:0
???? BodySize: 4
???? TypeID: 0x05
???? Stream ID: 0
負載格式:4字節(jié)整型表示,如5000000
建立流階段:
包括以下報文和步驟:
- 客戶端發(fā)起創(chuàng)建流請求
- 服務(wù)器響應(yīng)創(chuàng)建流結(jié)果
客戶端發(fā)起創(chuàng)建流請求:
協(xié)議截圖如下:

協(xié)議方向:客戶端 -> 服務(wù)器
塊頭字段:
???? HeaderType: 1
???? CSID: 3
???? 時間戳:0
???? BodySize: 25
???? TypeID: 0x14
負載格式:AMF0表示,createStream 2 object(Null)
服務(wù)器響應(yīng)創(chuàng)建流結(jié)果:
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 3
???? 時間戳:0
???? BodySize: 29
???? TypeID: 0x14
???? Stream ID: 0
負載格式:AMF0表示,_result 2 object(Null) Number(1)
播放階段:
包括以下報文和步驟:
- 客戶端發(fā)起播放節(jié)目請求
- 客戶端通知服務(wù)器設(shè)置緩沖區(qū)大小
- 服務(wù)器通知客戶端啟動流
- 服務(wù)器通知客戶端流進入播放狀態(tài)
- 服務(wù)器向客戶端發(fā)送媒體元數(shù)據(jù)
- 服務(wù)器向客戶端發(fā)送媒體數(shù)據(jù)
客戶端發(fā)起播放節(jié)目請求:
協(xié)議截圖如下:

協(xié)議方向:客戶端 -> 服務(wù)器
塊頭字段:
???? HeaderType: 0
???? CSID: 8
???? 時間戳:0
???? BodySize: 30
???? TypeID: 0x14
???? Stream ID: 1
負載格式:AMF0表示,play 4 Object(Null) String節(jié)目ID("a") Number開始時間(-2000)
客戶端通知服務(wù)器設(shè)置緩沖區(qū)大?。?/h6>
協(xié)議截圖如下:

協(xié)議方向:客戶端 -> 服務(wù)器
塊頭字段:
???? HeaderType: 1
???? CSID: 2
???? 時間戳:1
???? BodySize: 10
???? TypeID: 0x04
負載格式:Event Type,2字節(jié)的類型(3) 4字節(jié)的流ID(1) 4字節(jié)的MS時間單位(3000)
服務(wù)器通知客戶端啟動流:
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 2
???? 時間戳:0
???? BodySize: 6
???? TypeID: 0x04
負載格式:Event Type,2字節(jié)的類型(0) 4字節(jié)的流ID(1)
服務(wù)器通知客戶端流進入播放狀態(tài):
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 5
???? 時間戳:0
???? BodySize: 96
???? TypeID: 0x14
???? Stream ID: 1
負載格式:AMF0表示,onStatus 0 Object1(Null) object2
???? object2屬性列表:
???? ???? "level": "status"
???? ???? "code": "NetStream.Play.Start",
???? ???? "description": "Start live",
???? ???? End Of Object Marker
服務(wù)器向客戶端發(fā)送媒體元數(shù)據(jù):
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 5
???? 時間戳:0
???? BodySize: 387
???? TypeID: 0x12
???? Stream ID: 1
負載格式:AMF0表示,onMetaData object
???? object屬性列表:
???? ???? "Server": "NGINX RTMP"
???? ???? "width": 480,
???? ???? "height": 270,
???? ???? "displayWidth": 480,
???? ???? "displayHeight": 270,
???? ???? "duration": 0,
???? ???? "framerate": 16,
???? ???? "fps": 16,
???? ???? "videodatarate": 193,
???? ???? "videocodeid": 7,
???? ???? "audiodatarate": 52,
???? ???? "audiocodeid": 10,
???? ???? "profile": "",
???? ???? "level": "",
???? ???? End Of Object Marker
服務(wù)器向客戶端發(fā)送媒體數(shù)據(jù):
協(xié)議截圖如下:

協(xié)議方向:服務(wù)器 -> 客戶端
塊頭字段:
???? HeaderType: 0
???? CSID: 6
???? 時間戳:0
???? BodySize: 209
???? TypeID: 0x08
???? Stream ID: 1
負載格式:格式頭,媒體數(shù)據(jù)
流程時序:
結(jié)合以上分析,總結(jié)時序圖如下:

另外,關(guān)于HeaderType和CSID的運用,先歸納使用情況:
0x14(connect) HeaderType: 0 CSID: 3
0x05(Ack Window Size) HeaderType: 0 CSID: 2
0x06(BrandWidth) HeaderType: 0 CSID: 2
0x01(ChunkSize) HeaderType: 0 CSID: 2
0x14(connect _result) HeaderType: 0 CSID: 3
0x14(createStream) HeaderType: 1 CSID: 3
0x14(createStream _result) HeaderType: 0 CSID: 3
0x14(play) HeaderType: 0 CSID: 8
0x04(SetBufferMS) HeaderType: 1 CSID: 2
0x04(Stream Begin) HeaderType: 0 CSID: 2
0x14(play onStatus) HeaderType: 0 CSID: 5
0x12(onMetaData) HeaderType: 0 CSID: 5
0x08(audioData) HeaderType: 0 CSID: 6
0x09(videoData) HeaderType: 0 CSID: 7
總結(jié):
關(guān)于HeaderType的運用,有以下規(guī)則:
createStream使用1號HeaderType,借用3號CSID之前的StreamID。
SetBufferMS使用1號HeaderType。
audioData和videoData視情況使用0、1、2、3號HeaderType。
關(guān)于CSID的運用,有以下規(guī)則:
- 0x01~0x06命令用2號CSID。
- 大部分0x14命令用3號CSID,但有例外,如play用8號CSID,play onStatus用5號CSID。
- 0x12媒體元數(shù)據(jù)用5號CSID。
- 0x08音頻數(shù)據(jù)用6號CSID。
- 0x09視頻數(shù)據(jù)用7號CSID。