rtmp以TCP方式推流,分爲一個個消息包。
一、握手
握手開始於客戶端發送 C0,C1 塊。 服務端在發送 S0 和 S1 之前必須等待接收 C0,也可以等待接收 C1。 服務端在發送 S2 之前必須等待接收 C1。 在發送 C2 之前客戶端必須等待接收 S1 。 客戶端在發送任何數據之前必須等待接收 S2。 服務端在發送任何數據之前必須等待接收 C2。 |
1、 C0 : s <= c
客戶端RTMP的版本號,一個字節,一般是3
rtmp1.0規範所定義的版本是 3;0-2 是早期產品所用的,已被丟棄;4-31保留在未來使用;32-255 不允許使用(爲了區分其他以某一字符開始的文本協議)。
如果服務無法識別客戶端請求的版本,應該返回 3 。客戶端可以選擇減到版本 3 或選擇取消握手
2、 C1 : s <= c
一共1536字節
4-time + 4-zero + 1528-random
- 時間:4 字節 本字段包含時間戳。該時間戳應該是發送這個數據塊的端點的後續塊的時間起始點。可以是 0,或其他的 任何值。爲了同步多個流,端點可能發送其塊流的當前值。
- 零:4 字節 本字段必須是全零。
- 隨機數據:1528 字節。 本字段可以包含任何值。 因爲每個端點必須用自己初始化的握手和對端初始化的握 手來區分身份,所以這個數據應有充分的隨機性。但是並不需要加密安全的隨機值,或者動態值
3、 S0,S1,S2 : s => c
這裏可以一次性把S0,S1,S2 3個都發給客戶端。
S0 1 byte (服務端RTMP的版本號),範圍同C0
S1 1536 bytes (4-time + 4-zero + 1528-random),格式同C1
S2 1536 bytes (4-time + 4-zero + 1528-echo),是對C1的回覆,格式同C2
4、 C2 : s <= c
一共1536字節,是對S1的回覆
4-time + 4-time2 + 1528-echo
- 時間:4 字節 本字段必須包含對等段發送的時間(對 C2 來說是 S1,對 S2 來說是 C1)。
- 時間 2:4 字節 本字段必須包含先前發送的並被對端讀取的包的時間戳。
隨機回覆:1528 字節 本字段必須包含對端發送的隨機數據字段(對 C2 來說是 S1,對 S2 來說是 C1) 。 每個對等端可以用時間和時間 2 字段中的時間戳來快速地估計帶寬和延遲。
開始推送音視頻數據
二、推送音視頻數據
1、消息格式
- [Message Type ID] (1 bytes)
- [Payload Length] (1 bytes)
- [Time Stamp] (1 bytes)
- [Stream ID] (1 bytes)
- [Message Body]
[Message Type ID][Payload Length][Time Stamp][Stream ID] 這四部分稱爲 Message Header。
Message Type ID: 在1-7的消息用於協議控制,這些消息一般是RTMP協議自身管理要使用的消息,用戶一般情況下無需操作其中的數據。Message Type ID爲8,9的消息分別用於傳輸音頻和視頻數據。Message Type ID爲15-20的消息用於發送AMF編碼的命令,負責用戶與服務器之間的交互,比如播放,暫停等等。
Payload Length: 負載的長度
Time Stamp: 時間戳
Stream ID: 流的ID
2、消息塊拆包
一幀數據有時候會很大,比如幾十M甚至更大。但是爲了方便在網絡上傳輸,需要把數據拆分成一個個較小的塊,這裏稱之爲消息塊(Chunk)。
- [Chunk Basic Header]
- [Chunk Message Header]
- [Extended TimeStamp]
- [Chunk Data]
[Chunk Basic Header][Chunk Message Header][Extended TimeStamp] 稱之爲 Chunk Header。
2.1 Chunk Basic Header
Header Type + Channel ID (一共1-3個字節)
2.1.1 Header Type (FMT)
第一個字節的高2位決定[Chunk Message Header]的長度
- 00 12 bytes
- 01 8 bytes
- 10 4 bytes
- 11 1 byte
2.1.2 Channel ID
- 02 Ping 和ByteRead通道
- 03 Invoke通道 我們的connect() publish()和自字寫的NetConnection.Call() 數據都是在這個通道的
- 04 Audio和Vidio通道
- 05 06 07 服務器保留,經觀察FMS2用這些Channel也用來發送音頻或視頻數據
計算公式如下:
/**
* data : Basic Header Data
* fmt : Header Type
* cid : Channel ID
* return : Basic Header Data Length
*/
int rtmp_chunk_basic_header_read(const uint8_t* data, uint8_t* fmt, uint32_t* cid)
{
*fmt = data[0] >> 6;
*cid = data[0] & 0x3F;
if (0 == *cid)
{
*cid = 64 + (uint32_t)data[1];
return 2;
}
else if (1 == *cid)
{
*cid = 64 + (uint32_t)data[1] + ((uint32_t)data[2] << 8) /* 256 */;
return 3;
}
else
{
return 1;
}
}
2.2 Chunk Message Header
以最大fmt =00 length(Chunk Message Header) == 12 爲例
Chunk Message Header的結構是:
timestamp + message_length + message_type + msg_stream_id
其中message_type是一個枚舉變量:
- type爲1,2,3,5,6的時候是協議控制消息
- type爲4的時候表示 User Control Messages [Event_type + Event_Data] Event_type有Stream Begin,Stream End…
- type爲8,音頻數據
- type爲9,視頻數據
- type爲18 元數據消息[AMF0]
- type爲20 命令消息 Command Message(RPC Message)
These messages are sent to perform some operations like connect, createStream, publish, play, pause on the peer.
命令消息主要分成兩種NetConnection和NetStream。
connect,call,close,createStream命令可以在NetConnection中發送。
coonect(name,TranscationID,Command Object pair),play,publish,seek,pause等命令可以在NetStream中發送。
2.3 Extended TimeStamp
時間戳
2.3 Chunk Data
塊數據,接收方把塊數據組合成完整的一幀flv tag數據