1. handshake
1.1 概述
rtmp 連接從握手開始。它包含三個固定大小的塊。客戶端發送的三個塊命名為 C0,C1,C2;服務端發送的三個塊命名為
S0,S1,S2。
握手序列:
- 客戶端通過發送 C0 和 C1 消息來啟動握手過程。客戶端必須接收到 S1 消息,然后發送 C2 消息。客戶端必須接收到
S2 消息,然后發送其他數據。 - 服務端必須接收到 C0 或者 C1 消息,然后發送 S0 和 S1 消息。服務端必須接收到 C2 消息,然后發送其他數據。
握手示意圖
.
+-------------+ +-------------+
| Client | TCP/IP Network | Server |
+-------------+ | +-------------+
| | |
Uninitialized | Uninitialized
| C0 | |
|------------------->| C0 |
| |-------------------->|
| C1 | |
|------------------->| S0 |
| |<--------------------|
| | S1 |
Version sent |<--------------------|
| S0 | |
|<-------------------| |
| S1 | |
|<-------------------| Version sent
| | C1 |
| |-------------------->|
| C2 | |
|------------------->| S2 |
| |<--------------------|
Ack sent | Ack Sent
| S2 | |
|<-------------------| |
| | C2 |
| |-------------------->|
Handshake Done | Handshake Done
| | |
Pictorial Representation of Handshake
[譯]握手示意圖
1.2 complex handshake
1.2.1 C0 和 S0 格式
C0 和 S0 包由一個字節組成,下面是 C0/S0 包內的字段:
.
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| version |
+-+-+-+-+-+-+-+-+
C0 and S0 bits
- version(1 byte):RTMP 的版本,一般為 3。
1.2.2 C1 和 S1 格式
C1和S1包含兩部分數據:key和digest,分別為如下:
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| key (764 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| digest (764 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C1 and S1 bits
key 和 digest 的順序是不確定的,也有可能是:(nginx-rtmp中是如下的順序):
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| digest (764 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| key (764 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C1 and S1 bits
764 bytes key 結構:
- random-data: (offset) bytes
- key-data: 128 bytes
- random-data: (764 - offset - 128 - 4) bytes
- offset: 4 bytes
764 bytes digest 結構:
- offset: 4 bytes
- random-data: (offset) bytes
- digest-data: 32 bytes
- random-data: (764 - 4 - offset - 32) bytes
1.2.3 C2 和 S2 格式
.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random-data (1504 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| digest-data (32 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C2 and S2 bits
hanshake:S0 + S1 + S2
1.3 simple handshake
1.3.1 C0 和 S0 格式
C0 和 S0 包由一個字節組成,下面是 C0/S0 包內的字段:
.
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| version |
+-+-+-+-+-+-+-+-+
C0 and S0 bits
- version(1 byte):版本。在 C0 包內,這個字段代表客戶端請求的 RTMP 版本號。在 S0 包內,這個字段代表服務端選
擇的 RTMP 版本號。當前使用的版本是 3。版本 0-2 用在早期的產品中,如今已經棄用;版本 4-31 被預留用於后續產
品;版本 32-255 (為了區分 RTMP 協議和文本協議,文本協議通常是可以打印字符)不允許使用。如果服務器無法識別
客戶端的版本號,應該回復版本 3,。客戶端可以選擇降低到版本 3,或者終止握手過程。
1.3.2 C1 和 S1 格式
C1 和 S1 包長度為 1536 字節,包含以下字段:
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C1 and S1 bits
- time(4 bytes):本字段包含一個時間戳,客戶端應該使用此字段來標識所有流塊的時刻。時間戳取值可以為零或其他
任意值。為了同步多個塊流,客戶端可能希望多個塊流使用相同的時間戳。 - zero(4 bytes):本字段必須為零。
- random (1528 bytes):本字段可以包含任意數據。由於握手的雙方需要區分另一端,此字段填充的數據必須足夠隨機
(以防止與其他握手端混淆)。不過沒有必要為此使用加密數據或動態數據。
1.3.3 C2 和 S2 格式
C2 和 S2 包長度為 1536 字節,作為 C1 和 S1 的回應,包含以下字段:
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time2 (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C2 and S2 bits
- time(4 bytes):本字段必須包含對端發送的時間戳。
- time2(4 bytes):本字段必須包含時間戳,取值為接收對端發送過來的握手包的時刻。
- random(1528 bytes):本字段必須包含對端發送過來的隨機數據。握手的雙方可以使用時間 1 和時間 2 字段來估算
網絡連接的帶寬和/或延遲,但是不一定有用。
2. 組塊
2.1 塊格式
.
+--------------+----------------+--------------------+--------------+
| Basic Header | Message Header | Extended Timestamp | Chunk Data |
+--------------+----------------+--------------------+--------------+
| |
|<------------------- Chunk Header ----------------->|
Chunk Format
- 塊的基本頭(1-3字節):這個字段包含塊流ID和塊類型。塊類型決定了編碼過的消息頭的格式。這個字段是一個變
長字段,長度取決於塊流ID。 - 消息頭(0,3,7,11字節):這個字段包含被發送的消息信息(無論是全部,還是部分)。字段長度由塊頭中的
塊類型來決定。 - 擴展時間戳(0,4字節):這個字段是否存在取決於塊消息頭中編碼的時間戳。
- 塊數據(可變大小):當前塊的有效數據,上限為配置的最大塊大小。
2.2 Basic Header
包含 chunk stream ID(流通道id)和chunk type(即fmt),chunk stream id 一般被簡寫為CSID,用來唯一標識一個
特定的流通道,chunk type決定了后面Message Header的格式。Basic Header的長度可能是 1,2,或 3 個字節,
其中 chunk type 的長度是固定的(占2位,單位是bit),Basic Header 的長度取決於 CSID 的大小,在足夠存儲這兩
個字段的前提下最好用盡量少的字節從而減少由於引入Header增加的數據量。
RTMP協議支持用戶自定義 [3,65599] 之間的 CSID,0, 1, 2 由協議保留表示特殊信息。0 代表 Basic Header 總共要
占用 2 個字節,CSID 在 [64,319] 之間; 1 代表占用 3 個字節,CSID 在 [64,65599] 之間; 2 代表該 chunk 是控制
信息和一些命令信息。
2.2.1 Basic Header:1 byte
.
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+
2.2.2 Basic Header: 2 byte , csid == 0
CSID占14bit,此時協議將於chunk type所在字節的其他bit都置為0,剩下的一個字節表示CSID - 64,這樣共有8個bit
來存儲 CSID,8 bit 可以表示 [0,255] 個數,因此這種情況下 CSID 在 [64,319],其中 319 = 255 + 64。
.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 0 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.2.3 Basic Header: 3 bytes , csid == 1
CSID占22bit,此時協議將第一個字節的[2,8]bit置1,余下的16個bit表示CSID - 64,這樣共有16個bit來存儲CSID,
16bit可以表示[0,65535]共 65536 個數,因此這種情況下 CSID 在 [64,65599],其中65599=65535+64,需要注意的是,
Basic Header是采用小端存儲的方式,越往后的字節數量級越高,因此通過3個字節的每一個bit的值來計算CSID時,
應該是: <第三個字節的值> * 256 + <第二個字節的值> + 64.
.
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 1 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.3 Message Header
包含了要發送的實際信息(可能是完整的,也可能是一部分)的描述信息。Message Header的格式和長度取決於Basic Header
的chunk type,即fmt,共有四種不同的格式。其中第一種格式可以表示其他三種表示的所有數據,但由於其他三種格式是基
於對之前chunk的差量化的表示,因此可以更簡潔地表示相同的數據,實際使用的時候還是應該采用盡量少的字節表示相同意
義的數據。下面按字節從多到少的順序分別介紹這四種格式的 Message Header。
Message Header 四種消息頭格式。
一、Chunk Type(fmt) = 0:11 bytes
.
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (coutinue) |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type=0時Message Header占用11個字節,其他三種能表示的數據它都能表示,但在chunk stream 的開始第一個chunk和頭信息
中的時間戳后退(即值與上一個chunk相比減小,通常在回退播放的時候會出現這種情況)的時候必須采用這種格式。
- timestamp(時間戳):占用3個字節,因此它最多能表示到16777215=0xFFFFFF=2^24-1,當它
的值超過這個最大值時,這三個字節都置為1,這樣實際的timestamp會轉存到 Extended
Timestamp 字段中,接收端在判斷timestamp字段24個位都為1時就會去Extended Timestamp
中解析實際的時間戳。 - message length(消息數據長度):占用3個字節,表示實際發送的消息的數據如音頻幀、視頻
幀等數據的長度,單位是字節。注意這里是Message的長度,也就是chunk屬於的Message的總長
度,而不是chunk本身data的長度。 - message type id(消息的類型id):1個字節,表示實際發送的數據的類型,如8代表音頻數據,
9代表視頻數據。 - message stream id(消息的流id):4個字節,表示該chunk所在的流的ID,和Basic Header
的CSID一樣,它采用小端存儲方式。
二、Chunk Type(fmt) = 1:7 bytes
.
0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta |message length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| message length (coutinue) |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type為1時占用7個字節,省去了表示message stream id的4個字節,表示此chunk和上一次發的 chunk 所在的流相同,如果在
發送端和對端有一個流鏈接的時候可以盡量采取這種格式。
- timestamp delta:3 bytes,這里和type=0時不同,存儲的是和上一個chunk的時間差。類似
上面提到的timestamp,當它的值超過3個字節所能表示的最大值時,三個字節都置為1,實際
的時間戳差值就會轉存到Extended Timestamp字段中,接收端在判斷timestamp delta字段24
個bit都為1時就會去Extended Timestamp 中解析實際的與上次時間戳的差值。 - 其他字段與上面的解釋相同.
三、Chunk Type(fmt) = 2:3 bytes
.
0 1 2
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp delta |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type 為 2 時占用 3 個字節,相對於 type = 1 格式又省去了表示消息長度的3個字節和表示消息類型的1個字節,表示此 chunk
和上一次發送的 chunk 所在的流、消息的長度和消息的類型都相同。余下的這三個字節表示 timestamp delta,使用同type=1。
四、Chunk Type(fmt) = 3: 0 byte
type=3時,為0字節,表示這個chunk的Message Header和上一個是完全相同的。當它跟在type=0的chunk后面時,表示和前一
個 chunk 的時間戳都是相同。什么時候連時間戳都是相同呢?就是一個 Message 拆分成多個 chunk,這個 chunk 和上
一個 chunk 同屬於一個 Message。而當它跟在 type = 1或 type = 2 的chunk后面時的chunk后面時,表示和前一個 chunk
的時間戳的差是相同的。比如第一個 chunk 的 type = 0,timestamp = 100,第二個 chunk 的 type = 2,
timestamp delta = 20,表示時間戳為 100 + 20 = 120,第三個 chunk 的 type = 3,表示 timestamp delta = 20,
時間戳為 120 + 20 = 140。
2.4 Extended Timestamp(擴展時間戳)
在 chunk 中會有時間戳 timestamp 和時間戳差 timestamp delta,並且它們不會同時存在,只有這兩者之一大於3字節能表示的
最大數值 0xFFFFFF = 16777215 時,才會用這個字段來表示真正的時間戳,否則這個字段為 0。擴展時間戳占 4 個字節,
能表示的最大數值就是 0xFFFFFFFF = 4294967295。當擴展時間戳啟用時,timestamp字段或者timestamp delta要全置為1,
而不是減去時間戳或者時間戳差的值。
2.5 chunk 示例
2.5.1 chunk 示例1
本示例展示了一個音頻消息流。流中包含有冗余信息。
.
+---------+-----------------+-----------------+-----------------+
| |Message Stream ID| Message Type ID | Time | Length |
+---------+-----------------+-----------------+-------+---------+
| Msg # 1 | 12345 | 8 | 1000 | 32 |
+---------+-----------------+-----------------+-------+---------+
| Msg # 2 | 12345 | 8 | 1020 | 32 |
+---------+-----------------+-----------------+-------+---------+
| Msg # 3 | 12345 | 8 | 1040 | 32 |
+---------+-----------------+-----------------+-------+---------+
| Msg # 4 | 12345 | 8 | 1060 | 32 |
+---------+-----------------+-----------------+-------+---------+
Sample audio messages to be made into chunks
- 分析第一個 chunk:
- 首先包含第一個 Message 的 chunk 的 chunk type 為 0,因為它前面沒有可參考的 chunk,timestamp 為 1000,表示時間戳。
- type 為 0 的 header 占用 11 個字節,假定 chunk stream id 為 3 < 127,因此 basic header 占用 1 個字節;
- 再加上 data 的 32 字節,因此第一個 chunk 共 44 字節 = 11 + 1 + 32 個字節。
- 分析第二個 chunk:
- 第二個 chunk 和第一個 chunk 的 cs id 和 chunk type id,以及 data 的長度都相同,因此采用 類型 2;
- 可知 timestamp delta = 1020 - 1000 = 20;
- 因此第二個 chunk 占用 36 = 3(message header) + 1(basic header) + 32
- 分析第三個 chunk:
- 第三個 chunk 和第二個 chunk 的 cs id ,chunk type id,以及 data 的長度和時間戳的差值都相同,因此采用 類型 3,省去全部的 Message Header 的信息;
- 因此占用 33 = 1 + 32
- 分析第四個 chunk:
- 第四個 chunk 和第三個 chunk 情況相同,也占用 33 = 1 + 32 個字節。
最后實際發送的chunk如下面表格所示,該表格展示了由此音頻流產生的塊信息。從第 3 條信息開始,數據傳輸達到最大優
化。每條消息的頭部只增加了 1 字節長度。
.
+--------+---------+-----+------------+------- ---+------------+
| | Chunk |Chunk|Header Data |No.of Bytes|Total No.of |
| |Stream ID|Type | | After |Bytes in the|
| | | | |Header |Chunk |
+--------+---------+-----+------------+-----------+------------+
|Chunk#1 | 3 | 0 | delta: 1000| 32 | 44 |
| | | | length: 32,| | |
| | | | type: 8, | | |
| | | | stream ID: | | |
| | | | 12345 (11 | | |
| | | | bytes) | | |
+--------+---------+-----+------------+-----------+------------+
|Chunk#2 | 3 | 2 | 20 (3 | 32 | 36 |
| | | | bytes) | | |
+--------+---------+-----+----+-------+-----------+------------+
|Chunk#3 | 3 | 3 | none (0 | 32 | 33 |
| | | | bytes) | | |
+--------+---------+-----+------------+-----------+------------+
|Chunk#4 | 3 | 3 | none (0 | 32 | 33 |
| | | | bytes) | | |
+--------+---------+-----+------------+-----------+------------+
Format of each of the chunks of audio messages
2.5.2 chunk 示例2
本示例展示了一條長消息,由於消息的長度超過了塊的最大長度(128字節),此消息在傳輸時將被分割成若干個塊。
.
+-----------+-------------------+-----------------+-----------------+
| | Message Stream ID | Message Type ID | Time | Length |
+-----------+-------------------+-----------------+-----------------+
| Msg # 1 | 12346 | 9 (video) | 1000 | 307 |
+-----------+-------------------+-----------------+-----------------+
Sample Message to be broken to chunks
由表格知 data 的長度 307 > 128,因此這個 Message 要分割成幾個 chunk 發送:
- 第一個 chunk:type = 0,timestamp = 1000,承擔 128 個字節的 data,因此共占用 140 = 11 + 1 + 128 個字節。
- 第二個 chunk:同樣要發送 128 字節,其他字段(即Message Header 中的幾個字段)都與第一個相同,因此采用 類型 3,共 129 = 1 + 128 字節。
- 第三個 chunk:要發送的 data 的長度為 307 - 128 - 128 = 51 字節,還是采用 類型 3,共 1 + 51 = 52 字節。
下面是消息分割后產生的塊:
.
+-------+------+-----+-------------+-----------+------------+
| |Chunk |Chunk|Header |No. of |Total No. of|
| |Stream| Type|Data |Bytes after| bytes in |
| | ID | | | Header | the chunk |
+-------+------+-----+-------------+-----------+------------+
|Chunk#1| 4 | 0 | delta: 1000 | 128 | 140 |
| | | | length: 307 | | |
| | | | type: 9, | | |
| | | | stream ID: | | |
| | | | 12346 (11 | | |
| | | | bytes) | | |
+-------+------+-----+-------------+-----------+------------+
|Chunk#2| 4 | 3 | none (0 | 128 | 129 |
| | | | bytes) | | |
+-------+------+-----+-------------+-----------+------------+
|Chunk#3| 4 | 3 | none (0 | 51 | 52 |
| | | | bytes) | | |
+-------+------+-----+-------------+-----------+------------+
Format of each of the chunks
第一個塊的頭數據顯示了消息的長度為 307 字節。
在這兩個示例中,類型為 3 的塊有兩種使用方式。第一種是說明消息的繼續。第二種是說明新消息的頭信息可以由前面已經存
在的消息推到出來。
3. 協議控制消息
RTMP 塊流使用消息類型 ID 1、2、3、5、6 作為控制消息。這些消息包含了必要的 RTMP 塊流協議信息。
這些協議控制消息必須使用 0 作為消息流ID(作為已知的控制流ID),同時使用 2 作為塊流ID。協議控制消息接收立即生效;
解析時,時間戳字段被忽略。
3.1 設置塊大小 (1)
協議控制消息(1),設置塊大小,被用來通知對方新的最大的塊大小。
默認最大的塊大小為 128 字節,客戶端和服務器可以使用此消息來修改默認的塊大小。例如,假設客戶端想要發送的音頻數據
大小為131 字節,而塊大小為 128 字節。在這種情況下,客戶端可以通知服務器新的塊大小為 131 字節,然后就可以使用一
個塊來發送完整的音頻數據了。
最大的塊大小至少為 128 字節,塊至少攜帶 1 個字節的內容。通信的每一個方向(例如從客戶端到服務器)擁有獨立的塊大小
設置。
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| chunk size (31 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Set Chunk Size’ protocol message
- 0:當前比特位必須為零。
- chunk size(31 bits): This field holds the new maximum chunk size, in bytes, which will be used for all of the sender's
subsequent chunks until further notice. Valid sizes are 1 to 2147483647(0x7FFFFFFF) inclusive; however, all sizes
greater than 16777215(0xFFFFFF) are equivalent since no chunk is larger than onemessage, and no message is larger than
16777215 bytes. - 塊大小(31比特):本字段標識了新的最大塊大小,以字節為單位,發送端之后將使用此值作為最大的塊大小。本字段的
有效值為 1 - 2147483647(0x7FFFFFFF),由於消息的最大長度為 16777215(0xFFFFFF),而一個塊最多只能攜帶一條消
息,因此本字段的實際有效值為 1~16777215(0xFFFFFF)。
send chunk size
3.2 中斷消息 (2)
協議控制消息(2),中斷消息,用來通知通信的對方,如果正在等待一條消息的部分塊(已經接收了一部分),那么可以丟棄
之前已經接收到的塊。通信的一方將接收到塊流ID作為當前協議消息的有效數據。應用程序可以發送此消息來通知對方,當前
正在傳輸的消息沒有必要再處理了。
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk stream id (32 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Abort Message’ protocol message
- 塊流ID(32比特):本字段包含了塊流ID,用來標識哪個塊流ID的消息將被丟棄。
3.3 應答(3)
客戶端和服務器在接收到與接收窗口大小相等的數據后,必須發送應答消息給對方。窗口大小的定義為發送方在接收到接收方
的任何應答前,可以發送的最大數據量。本消息包含了序列號,序列號為截至目前接收到的數據總和,以字節為單位。
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sequence number (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Acknowledgement’ protocol message
- 序列號(32比特):本字段包含了截止目前接收到的數據總和,以字節為單位。
3.4 應答窗口大小(5)
客戶端和服務器發送這個消息來通知對方應答窗口的大小。發送方在發送了等於窗口大小的數據之后,等待接收對方的應答消
息(在接收到應答之前停止發送數據)。接收方必須發送應答消息,在會話開始時,或從上一次發送應答之后接收到了等於窗
口大小的數據。
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgement Window size (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Payload for the ‘Window Acknowledgement Size’ protocol message
send ack_size
3.5 設置流帶寬(6)
客戶端和服務器發送此消息來說明對方的出口帶寬限制。接收方以此來限制自己的出口帶寬,即限制未被應答的消息數據大
小。接收到此消息的一方,如果窗口大小與上次發送的不一致,應該回復應答窗口大小的消息。
.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgement Window size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Limit Type |
+-+-+-+-+-+-+-+-+
Payload for the ‘Set Peer Bandwidth’ protocol message
限制類型的取值為下面之一:
- 硬限制(0):應該限制出口帶寬為指明的窗口大小。
- 軟限制(1):應該限制出口帶寬為指明的窗口大小,或已經生效的小一點的窗口大小。
- 動態限制(2):如果上一次為硬限制,此消息被視為硬限制,否則忽略此消息。
send bandwidth
4. Command Message (17 或 20)
Command Message(命令消息,Message Type ID = 17 或 20):表示在客戶端和服務器間傳遞的在對端執行某些操作的命令
消息,connect 表示連接對端,對端如果同意連接的話就會記錄發送端信息並返回連接成功消息,publish 表示開始向對方
推流,接收端接收到命令后准備好接收對端發送的流信息。當信息使用 AMF0 編碼時,Message Type ID = 20,AMF3 編碼
時 為 17。
服務器和客戶端之間使用 AMF 編碼的命令消息交互。 一些命令消息被用來發送操作指令,比如 connect,createStream,
public,play,pause。另外一些命令消息被用來通知發送方請求命令的狀態,比如 onstatus,result 等。一條命令消息
包括命令對稱、交互 ID、包含相關參數的命令對象。服務器和客戶端通過在創建的流中遠程調用的方式,使用命令消息來
進行交互。
服務器發送給客戶端的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | _result or _error; indicates whether |
| | | the response is result or error. |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID is 1 for connect |
| ID | | responses |
| | | |
+--------------+----------+----------------------------------------+
| Properties | Object | Name-value pairs that describe the |
| | | properties(fmsver etc.) of the |
| | | connection. |
+--------------+----------+----------------------------------------+
| Information | Object | Name-value pairs that describe the |
| | | response from|the server. ’code’, |
| | | ’level’, ’description’ are names of few|
| | | among such information. |
+--------------+----------+----------------------------------------+
命令執行過程中的消息流如下:
- 客戶端發送連接命令給服務器,獲得與服務器連接的實例。
- 服務器在接收到連接命令后,發送應答窗口大小的消息給客戶端。同時與連接命令中接到的應用建立連接。
- 服務器發送設置流帶寬消息給客戶端。
- 客戶端在接收並處理了設置流帶寬的消息后,發送應答窗口大小的消息給服務器。
- 服務器接着發送開始流的用戶控制消息給客戶端。
- 服務器發送 result 命令消息給客戶端,通知連接狀態是成功或失敗。命令消息中包含了事務ID。消息中還包含了像 FMS
版本之類的屬性,以及級別,編碼,描述,對象編碼等信息。
+--------------+ +-------------+
| Client | | | Server |
+------+-------+ | +------+------+
| Handshaking done |
| | |
| | |
| | |
| | |
|----------- Command Message(connect) ------->|
| |
|<------- Window Acknowledgement Size --------|
| |
|<----------- Set Peer Bandwidth -------------|
| |
|-------- Window Acknowledgement Size ------->|
| |
|<------ User Control Message(StreamBegin) ---|
| |
|<------------ Command Message ---------------|
| (_result- connect response) |
| |
Message flow in the connect command
4.1 命令類型
客戶端和服務器通過 AMF 編碼的數據交換命令。發送者發送包含命令名稱,事務ID,包含相關參數的命令對象的消息。例如,
通過連接命令中包含的 APP 參數來告訴服務器連接的對方是哪個客戶端。接收方處理命令消息,並使用相同的事務ID應答。
應答字符串為 _result 或 _error 或方法名,例如 verifyClient 或 contactExternalServer。事務 ID 標明了應答指向的
命令。事務ID相當於 IMAP 協議或其他協議中的標簽。命令字符串中的方法名,表明了發送端想要在接收端執行的方法。
下面的類對象被用來發送各種命令:
- NetConnection:服務器和客戶端之間進行網絡連接的一種高級表示形式。
- NetStream:代表了發送音頻流,視頻流,或其他相關數據的頻道。當然還有一些像播放,暫停之類的命令,用來控制數
據流。
4.2 網絡連接命令
網絡連接管理着客戶端和服務器之間的雙向連接。另外,它也支持異步遠程命令調用。
網絡連接允許使用以下的命令:
- 連接 connect
- 調用 call
- 停止 close
- 創建流 createStream
4.2.1 connect: 連接
客戶端發送連接命令給服務器,來獲取一個和服務器通信的實例。客戶端發送給服務器的命令結構如下:
.
+----------------+---------+---------------------------------------+
| Field Name | Type | Description |
+--------------- +---------+---------------------------------------+
| Command Name | String | Name of the command. Set to "connect".|
+----------------+---------+---------------------------------------+
| Transaction ID | Number | Always set to 1. |
+----------------+---------+---------------------------------------+
| Command Object | Object | Command information object which has |
| | | the name-value pairs. |
+----------------+---------+---------------------------------------+
| Optional User | Object | Any optional information |
| Arguments | | |
+----------------+---------+---------------------------------------+
下面是連接命令的命令對象里包含的鍵值對的說明:
.
+-----------+--------+-----------------------------+----------------+
| Property | Type | Description | Example Value |
+-----------+--------+-----------------------------+----------------+
| app | String | The Server application name | testapp |
| | | the client is connected to. | |
+-----------+--------+-----------------------------+----------------+
| flashver | String | Flash Player version. It is | FMSc/1.0 |
| | | the same string as returned | |
| | | by the ApplicationScript | |
| | | getversion () function. | |
+-----------+--------+-----------------------------+----------------+
| swfUrl | String | URL of the source SWF file | file://C:/ |
| | | making the connection. | FlvPlayer.swf |
+-----------+--------+-----------------------------+----------------+
| tcUrl | String | URL of the Server. | rtmp://local |
| | | It has the following format.| host:1935/test |
| | | protocol://servername:port/ | app/instance1 |
| | | appName/appInstance | |
+-----------+--------+-----------------------------+----------------+
| fpad | Boolean| True if proxy is being used.| true or false |
+-----------+--------+-----------------------------+----------------+
|audioCodecs| Number | Indicates what audio codecs | SUPPORT_SND |
| | | the client supports. | _MP3 |
+-----------+--------+-----------------------------+----------------+
|videoCodecs| Number | Indicates what video codecs | SUPPORT_VID |
| | | are supported. | _SORENSON |
+-----------+--------+-----------------------------+----------------+
|videoFunct-| Number | Indicates what special video| SUPPORT_VID |
|ion | | functions are supported. | _CLIENT_SEEK |
+-----------+--------+-----------------------------+----------------+
| pageUrl | String | URL of the web page from | http:// |
| | | where the SWF file was | somehost/ |
| | | loaded. | sample.html |
+-----------+--------+-----------------------------+----------------+
| object | Number | AMF encoding method. | AMF3 |
| Encoding | | | |
+-----------+--------+-----------------------------+----------------+
音頻編碼屬性的可選值:
- 原始 PCM,ADPCM,MP3,NellyMoser(5,8,11,16,22,44kHz),AAC,Speex。
.
+----------------------+----------------------------+--------------+
| Codec Flag | Usage | Value |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_NONE | Raw sound, no compression | 0x0001 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_ADPCM | ADPCM compression | 0x0002 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_MP3 | mp3 compression | 0x0004 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_INTEL | Not used | 0x0008 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_UNUSED | Not used | 0x0010 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_NELLY8 | NellyMoser at 8-kHz | 0x0020 |
| | compression | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_NELLY | NellyMoser compression | 0x0040 |
| | (5, 11, 22, and 44 kHz) | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_G711A | G711A sound compression | 0x0080 |
| | (Flash Media Server only) | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_G711U | G711U sound compression | 0x0100 |
| | (Flash Media Server only) | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_NELLY16 | NellyMouser at 16-kHz | 0x0200 |
| | compression | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_AAC | Advanced audio coding | 0x0400 |
| | (AAC) codec | |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_SPEEX | Speex Audio | 0x0800 |
+----------------------+----------------------------+--------------+
| SUPPORT_SND_ALL | All RTMP-supported audio | 0x0FFF |
| | codecs | |
+----------------------+----------------------------+--------------+
視頻編碼屬性的可選值:
- Sorenson,V1,On2,V2,H264.
.
+----------------------+----------------------------+--------------+
| Codec Flag | Usage | Value |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_UNUSED | Obsolete value | 0x0001 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_JPEG | Obsolete value | 0x0002 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_SORENSON | Sorenson Flash video | 0x0004 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_HOMEBREW | V1 screen sharing | 0x0008 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_VP6 (On2)| On2 video (Flash 8+) | 0x0010 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_VP6ALPHA | On2 video with alpha | 0x0020 |
| (On2 with alpha | channel | |
| channel) | | |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_HOMEBREWV| Screen sharing version 2 | 0x0040 |
| (screensharing v2) | (Flash 8+) | |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_H264 | H264 video | 0x0080 |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_ALL | All RTMP-supported video | 0x00FF |
| | codecs | |
+----------------------+----------------------------+--------------+
視頻函數屬性的可選值:
.
+----------------------+----------------------------+--------------+
| Function Flag | Usage | Value |
+----------------------+----------------------------+--------------+
| SUPPORT_VID_CLIENT | Indicates that the client | 1 |
| _SEEK | can perform frame-accurate | |
| | seeks. | |
+----------------------+----------------------------+--------------+
對象編碼屬性的可選值:
.
+----------------------+----------------------------+--------------+
| Encoding Type | Usage | Value |
+----------------------+----------------------------+--------------+
| AMF0 | AMF0 object encoding | 0 |
| | supported by Flash 6 and | |
| | later | |
+----------------------+----------------------------+--------------+
| AMF3 | AMF3 encoding from | 3 |
| | Flash 9 (AS3) | |
+----------------------+----------------------------+--------------+
示例
C -> S: 服務器接收客戶端 connect 命令消息
S -> C: 服務器響應客戶端 connect 成功消息
4.2.2 call: 調用
網絡連接對象中包含的 call 方法,會在接收端執行遠程過程調用(RPC)。被調用的 RPC 方法名作為 call 方法的參數傳輸。
從發送端到接收端的命令結構如下:
.
+--------------+----------+----------------------------------------+
|Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Procedure | String | Name of the remote procedure that is |
| Name | | called. |
+--------------+----------+----------------------------------------+
| Transaction | Number | If a response is expected we give a |
| | | transaction Id. Else we pass a value of|
| ID | | 0 |
+--------------+----------+----------------------------------------+
| Command | Object | If there exists any command info this |
| Object | | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
| Optional | Object | Any optional arguments to be provided |
| Arguments | | |
+--------------+----------+----------------------------------------+
應答的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command. |
| | | |
+--------------+----------+----------------------------------------+
| Transaction | Number | ID of the command, to which the |
| ID | | response belongs.
+--------------+----------+----------------------------------------+
| Command | Object | If there exists any command info this |
| Object | | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
| Response | Object | Response from the method that was |
| | | called. |
+------------------------------------------------------------------+
4.2.3 createStream: 創建流
客戶端通過發送此消息給服務器來創建一個用於消息交互的邏輯通道。音頻,視頻,和元數據都是通過 createStream 命令創建
的流通道發布出去的。
NetConnection 是默認的交互通道,流 ID 為0. 協議和一部分命令消息,包含 createStream,都是使用默認的交互通道發布
的。
從客戶端發送給服務器的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command. Set to |
| | | "createStream". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID of the command. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Object | If there exists any command info this |
| Object | | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
從服務器發送給客戶端的命令結構:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | _result or _error; indicates whether |
| | | the response is result or error. |
+--------------+----------+----------------------------------------+
| Transaction | Number | ID of the command that response belongs|
| ID | | to. |
+--------------+----------+----------------------------------------+
| Command | Object | If there exists any command info this |
| Object | | is set, else this is set to null type. |
+--------------+----------+----------------------------------------+
| Stream | Number | The return value is either a stream ID |
| ID | | or an error information object. |
+--------------+----------+----------------------------------------+
示例
C -> S: 服務器接收客戶端 createStream 命令消息
C -> S: 服務器響應客戶端 createStream 成功消息
4.3 網絡流命令
網絡流定義了通過網絡連接把音頻,視頻和數據消息流在客戶端和服務器之間進行交換的通道。一個網絡連接對象可以有多個
網絡流,進而支持多個數據流。
客戶端可以通過網絡流發送到服務器的命令如下:
- 播放play
- 播放2 play2
- 刪除流 deleteStream
- 關閉流 closeStream
- 接收音頻 receiveAudio
- 接收視頻 receiveVideo
- 發布 publish
- 定位 seek
- 暫停 pause
服務器通過發送 onStatus 命令給客戶端來通知網絡流狀態的更新。
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | The command name "onStatus". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | There is no command object for |
| Object | | onStatus messages. |
+--------------+----------+----------------------------------------+
| Info Object | Object | An AMF object having at least the |
| | | following three properties: "level" |
| | | (String): the level for this message, |
| | | one of "warning", "status", or "error";|
| | | "code" (String): the message code, for |
| | | example "NetStream.Play.Start"; and |
| | | "description" (String): a human- |
| | | readable description of the message. |
| | | The Info object MAY contain other |
| | | properties as appropriate to the code. |
+--------------+----------+----------------------------------------+
Format of NetStream status message commands.
4.3.1 play: 播放
客戶端發送此命令來通知服務器開始播放流。多次使用此命令可以創建一個播放列表。如果想要創建一個動態播放列表來在不
同的直播或點播流之間切換,可以通過多次調用播放命令,同時將 Reset 字段設置為 false。相反,如果想要立即播放指定
的流,先清理掉之前的播放隊列,再調用播放命令,同時將 Reset 字段設置為 true。
從客戶端發送給服務器的命令結構如下:
.
+--------------+----------+-----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+-----------------------------------------+
| Command Name | String | Name of the command. Set to "play". |
+--------------+----------+-----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+-----------------------------------------+
| Command | Null | Command information does not exist. |
| Object | | Set to null type. |
+--------------+----------+-----------------------------------------+
| Stream Name | String | Name of the stream to play. |
| | | To play video (FLV) files, specify the |
| | | name of the stream without a file |
| | | extension (for example, "sample"). To |
| | | play back MP3 or ID3 tags, you must |
| | | precede the stream name with mp3: |
| | | (for example, "mp3:sample". To play |
| | | H.264/AAC files, you must precede the |
| | | stream name with mp4: and specify the |
| | | file extension. For example, to play the|
| | | file sample.m4v,specify "mp4:sample.m4v"|
| | | |
+--------------+----------+-----------------------------------------+
| Start | Number | An optional parameter that specifies |
| | | the start time in seconds. The default |
| | | value is -2, which means the subscriber |
| | | first tries to play the live stream |
| | | specified in the Stream Name field. If a|
| | | live stream of that name is not found,it|
| | | plays the recorded stream of the same |
| | | name. If there is no recorded stream |
| | | with that name, the subscriber waits for|
| | | a new live stream with that name and |
| | | plays it when available. If you pass -1 |
| | | in the Start field, only the live stream|
| | | specified in the Stream Name field is |
| | | played. If you pass 0 or a positive |
| | | number in the Start field, a recorded |
| | | stream specified in the Stream Name |
| | | field is played beginning from the time |
| | | specified in the Start field. If no |
| | | recorded stream is found, the next item |
| | | in the playlist is played. |
+--------------+----------+-----------------------------------------+
| Duration | Number | An optional parameter that specifies the|
| | | duration of playback in seconds. The |
| | | default value is -1. The -1 value means |
| | | a live stream is played until it is no |
| | | longer available or a recorded stream is|
| | | played until it ends. If you pass 0, it |
| | | plays the single frame since the time |
| | | specified in the Start field from the |
| | | beginning of a recorded stream. It is |
| | | assumed that the value specified in |
| | | the Start field is equal to or greater |
| | | than 0. If you pass a positive number, |
| | | it plays a live stream for |
| | | the time period specified in the |
| | | Duration field. After that it becomes |
| | | available or plays a recorded stream |
| | | for the time specified in the Duration |
| | | field. (If a stream ends before the |
| | | time specified in the Duration field, |
| | | playback ends when the stream ends.) |
| | | If you pass a negative number other |
| | | than -1 in the Duration field, it |
| | | interprets the value as if it were -1. |
+--------------+----------+-----------------------------------------+
| Reset | Boolean | An optional Boolean value or number |
| | | that specifies whether to flush any |
| | | previous playlist. |
+--------------+----------+-----------------------------------------+
play 播放命令執行流程:
.
+-------------+ +----------+
| Play Client | | | Server |
+------+------+ | +-----+----+
| Handshaking and Application |
| connect done |
| | |
| | |
| | |
| | |
---+---- |------Command Message(createStream) ----->|
Create| | |
Stream| | |
---+---- |<---------- Command Message --------------|
| (_result- createStream response) |
| |
---+---- |------ Command Message (play) ----------->|
| | |
| |<------------ SetChunkSize --------------|
| | |
| |<---- User Control (StreamIsRecorded) ----|
Play | | |
| |<---- User Control (StreamBegin) ---------|
| | |
| |<--Command Message(onStatus-play reset) --|
| | |
| |<--Command Message(onStatus-play start) --|
| | |
| |<-------------Audio Message---------------|
| | |
| |<-------------Video Message---------------|
| | | |
|
Keep receiving audio and video stream till finishes
Message flow in the play command
命令執行過程中的消息流如下:
- 當客戶端接收到服務器返回的 createStream 成功的消息時,開始發送播放命令。
- 服務器接收到播放命令后,發送設置塊大小的消息。
- 服務器發送一條用戶控制消息,消息內包含了 StreamlsRecorded 事件和流ID。事件類型位於消息的前 2 個字節,流 ID
位於消息的最后 4 個字節。 - 服務器發送一條用戶控制消息,消息內包含了 StreamBegin 事件,用於通知客戶端開始播放流。
- 如果客戶端已經成功發送了播放命令,那么服務器發送兩條 onStatus 命令給客戶端,命令的內容為 NetStream.Play.Start
和 NetStream.Play.Reset。服務器只有在客戶端發送了設置有重置標簽的播放命令后,才能發送 NetStream.Play.Reset
命令。如果服務器找不到客戶端請求播放的流,那么發送 NetStream.Play.StreamNotFound 命令給客戶端。 - 之后,服務器發送音頻和視頻數據給客戶端。
4.3.2 play2: 播放2
與播放命令的不同之處在於,播放 2 命令可以在不修改播放內容時間線的前提下切換到一個不同碼率的流。服務器包含了多個
不同碼率的流文件用於支持客戶端的播放 2 請求。
從客戶端發送給服務器的命令結構如下:
.
The command structure from the client to the server is as follows:
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to "play2". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | Command information does not exist. |
| Object | | Set to null type. |
+--------------+----------+----------------------------------------+
| Parameters | Object | An AMF encoded object whose properties |
| | | are the public properties described |
| | | for the flash.net.NetStreamPlayOptions |
| | | ActionScript object. |
+--------------+----------+----------------------------------------+
有關 NetStreamPlayOptions 對象的公開屬性的說明詳見 AS3 語言的文檔。
此命令的消息流如下所示:
.
+--------------+ +-------------+
| Play2 Client | | | Server |
+--------+-----+ | +------+------+
| Handshaking and Application |
| connect done |
| | |
| | |
| | |
| | |
---+---- |---- Command Message(createStream) --->|
Create | | |
Stream | | |
---+---- |<---- Command Message (_result) -------|
| |
---+---- |------ Command Message (play) -------->|
| | |
| |<------------ SetChunkSize ------------|
| | |
| |<--- UserControl (StreamIsRecorded)----|
Play | | |
| |<------- UserControl (StreamBegin)-----|
| | |
| |<--Command Message(onStatus-playstart)-|
| | |
| |<---------- Audio Message -------------|
| | |
| |<---------- Video Message -------------|
| | |
| |
---+---- |-------- Command Message(play2) ------>|
| | |
| |<------- Audio Message (new rate) -----|
Play2 | | |
| |<------- Video Message (new rate) -----|
| | | |
| | | |
| Keep receiving audio and video stream till finishes
|
Message flow in the play2 command
4.3.3 deleteStream: 刪除流
如果需要銷毀網絡流對象,可以通過網絡流發送刪除流消息給服務器。
客戶端發送給服務器的命令結構如下:
.
The command structure from the client to the server is as follows:
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to |
| | | "deleteStream". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | Command information object does not |
| Object | | exist. Set to null type. |
+--------------+----------+----------------------------------------+
| Stream ID | Number | The ID of the stream that is destroyed |
| | | on the server. |
+--------------+----------+----------------------------------------+
服務器接收到此消息后,不做任何回復。
4.3.4 receiveAudio: 接收音頻
網絡流發送此消息通知服務器,是否要發送音頻數據給客戶端。
.
The command structure from the client to the server is as follows:
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to |
| | | "receiveAudio". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | Command information object does not |
| Object | | exist. Set to null type. |
+--------------+----------+----------------------------------------+
| Bool Flag | Boolean | true or false to indicate whether to |
| | | receive audio or not. |
+--------------+----------+----------------------------------------+
如果服務器接收到帶有 fasle 標簽的消息后,不做任何回復。如果接收到帶有 true 標簽的消息,服務器回復帶有
NetStream.Seek.Notify 和 NetStream.Play.Start 的消息給客戶端。
4.3.5 receiveVideo: 接收視頻
網絡流發送此消息通知服務器,是否要發送視頻數據給客戶端。
客戶端發送給服務器的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to |
| | | "receiveVideo". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | Command information object does not |
| Object | | exist. Set to null type. |
+--------------+----------+----------------------------------------+
| Bool Flag | Boolean | true or false to indicate whether to |
| | | receive video or not. |
+--------------+----------+----------------------------------------+
如果服務器接收到帶有 false 標簽的消息后,不做任何回復。如果接收到帶有 true 標簽的消息,服務器回復帶有
NetStream.Seek.Notify 和 NetStream.Play.Start 的消息給客戶端。
4.3.6 publish: 發布
客戶端發送此消息,用來發布一個有名字的流到服務器。其他客戶端可以使用此流名來播放流,接收發布的音頻,視頻,
以及其他數據消息。
客戶端發送給服務器的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to "publish". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | Command information object does not |
| Object | | exist. Set to null type. |
+--------------+----------+----------------------------------------+
| Publishing | String | Name with which the stream is |
| Name | | published. |
+--------------+----------+----------------------------------------+
| Publishing | String | Type of publishing. Set to "live", |
| Type | | "record", or "append". |
| | | record: The stream is published and the|
| | | data is recorded to a new file.The file|
| | | is stored on the server in a |
| | | subdirectory within the directory that |
| | | contains the server application. If the|
| | | file already exists, it is overwritten.|
| | | append: The stream is published and the|
| | | data is appended to a file. If no file |
| | | is found, it is created. |
| | | live: Live data is published without |
| | | recording it in a file. |
+--------------+----------+----------------------------------------+
服務器接收到此消息后,回復 onStatus 命令來標記發布的開始。
示例1:發布錄制的視頻
此示例闡述了發布者如果發布視頻流到服務器。其他客戶端可以訂閱並播放此視頻流。
.
+--------------------+ +-----------+
| Publisher Client | | | Server |
+----------+---------+ | +-----+-----+
| Handshaking Done |
| | |
| | |
---+---- |----- Command Message(connect) ----->|
| | |
| |<----- Window Acknowledge Size ------|
Connect | | |
| |<-------Set Peer BandWidth ----------|
| | |
| |------ Window Acknowledge Size ----->|
| | |
| |<------User Control(StreamBegin)-----|
| | |
---+---- |<---------Command Message -----------|
| (_result- connect response) |
| |
---+---- |--- Command Message(createStream)--->|
Create | | |
Stream | | |
---+---- |<------- Command Message ------------|
| (_result- createStream response) |
| |
---+---- |---- Command Message(publish) ------>|
| | |
| |<------User Control(StreamBegin)-----|
| | |
| |-----Data Message (Metadata)-------->|
| | |
Publishing| |------------ Audio Data ------------>|
Content | | |
| |------------ SetChunkSize ---------->|
| | |
| |<----------Command Message ----------|
| | (_result- publish result) |
| | |
| |------------- Video Data ----------->|
| | | |
| | | |
| Until the stream is complete |
| | |
Message flow in publishing a video stream
示例2:從錄制的流發布元數據
本示例展示了發布元數據的消息交換過程。
.
+------------------+ +---------+
| Publisher Client | | | FMS |
+---------+--------+ | +----+----+
| Handshaking and Application |
| connect done |
| | |
| | |
---+--- |---Command Messsage(createStream) -->|
Create | | |
Stream | | |
---+--- |<---------Command Message------------|
| (_result - command response) |
| |
---+--- |---- Command Message(publish) ------>|
Publishing | | |
metadata | |<------ UserControl(StreamBegin)-----|
from file | | |
| |-----Data Message (Metadata) ------->|
| |
Publishing metadata
4.3.7 seek: 定位
客戶端發送此消息來定位多媒體文件或播放列表的偏移(以毫秒為單位)。
客戶端發送給服務器的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to "seek". |
+--------------+----------+----------------------------------------+
| Transaction | Number | Transaction ID set to 0. |
| ID | | |
+--------------+----------+----------------------------------------+
| Command | Null | There is no command information object |
| Object | | for this command. Set to null type. |
+--------------+----------+----------------------------------------+
| milliSeconds | Number | Number of milliseconds to seek into |
| | | the playlist. |
+--------------+----------+----------------------------------------+
當定位完成后,服務器回復 NetStream.Seek.Notify 狀態消息給客戶端。如果定位失敗,將回復 _error 消息。
4.3.8 pause: 暫停
客戶端發送此消息來通知服務器暫停或開始播放。
客戶端發送給服務器的命令結構如下:
.
+--------------+----------+----------------------------------------+
| Field Name | Type | Description |
+--------------+----------+----------------------------------------+
| Command Name | String | Name of the command, set to "pause". |
+--------------+----------+----------------------------------------+
| Transaction | Number | There is no transaction ID for this |
| ID | | command. Set to 0. |
+--------------+----------+----------------------------------------+
| Command | Null | Command information object does not |
| Object | | exist. Set to null type. |
+--------------+----------+----------------------------------------+
|Pause/Unpause | Boolean | true or false, to indicate pausing or |
| Flag | | resuming play |
+--------------+----------+----------------------------------------+
| milliSeconds | Number | Number of milliseconds at which the |
| | | the stream is paused or play resumed. |
| | | This is the current stream time at the |
| | | Client when stream was paused. When the|
| | | playback is resumed, the server will |
| | | only send messages with timestamps |
| | | greater than this value. |
+--------------+----------+----------------------------------------+
當流暫停成功,服務器發送 NetStream.Pause.Notify 狀態消息給客戶端,如果流未暫停,服務器發送
NetStream.Unpause.Notify 狀態消息給客戶端。如果暫停失敗,則發送 _error 消息。
5. Data Message ( 15 或 18)
Data Message(數據消息,Message Type ID = 15 或 18):傳遞一些元數據(Metadata,比如視頻名,分辨率等等)或者用
戶自定義的一些消息。當信息使用 AMF0 編碼時,Message Type ID = 18,AMF3 則為15.
元數據包含了(音視頻)數據的細節信息,像流的創建時間,時間點,主題等等.
6. Shared Object Message (16 或 19)
Shared Object Message(共享消息,Message Type ID = 16 或 19):表示一個 Flash 類型的對象,由鍵值對的集合組成,用
於多客戶端,多實例時使用。AMF0 時 Message Type ID 為 19,AMF3 為 16。每個消息可以包含多個事件。
共享消息格式:
.
+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
|Header|Shared|Current|Flags|Event|Event |Event|.|Event|Event |Event|
| |Object|Version| |Type |data |data |.|Type |data |data |
| |Name | | | |length| |.| |length| |
+------+------+-------+-----+-----+------+-----+ +-----+------+-----+
| |
|<- - - - - - - - - - - - - - - - - - - - - - - - - - - - - >|
| AMF Shared Object Message body |
The shared object message format
下面是共享消息支持的事件類型:
.
+---------------+--------------------------------------------------+
| Event | Description |
+---------------+--------------------------------------------------+
| Use(=1) | The client sends this event to inform the server |
| | about the creation of a named shared object. |
+---------------+--------------------------------------------------+
| Release(=2) | The client sends this event to the server when |
| | the shared object is deleted on the client side. |
+---------------+--------------------------------------------------+
| Request Change| The client sends this event to request that the |
| (=3) | change the value associated with a named |
| | parameter of the shared object. |
+---------------+--------------------------------------------------+
| Change (=4) | The server sends this event to notify all |
| | clients, except the client originating the |
| | request, of a change in the value of a named |
| | parameter. |
+---------------+--------------------------------------------------+
| Success (=5) | The server sends this event to the requesting |
| | client in response to RequestChange event if the |
| | request is accepted. |
+---------------+--------------------------------------------------+
| SendMessage | The client sends this event to the server to |
| (=6) | broadcast a message. On receiving this event, |
| | the server broadcasts a message to all the |
| | clients, including the sender. |
+---------------+--------------------------------------------------+
| Status (=7) | The server sends this event to notify clients |
| | about error conditions. |
+---------------+--------------------------------------------------+
| Clear (=8) | The server sends this event to the client to |
| | clear a shared object. The server also sends |
| | this event in response to Use event that the |
| | client sends on connect. |
+---------------+--------------------------------------------------+
| Remove (=9) | The server sends this event to have the client |
| | delete a slot. |
+---------------+--------------------------------------------------+
| Request Remove| The client sends this event to have the client |
| (=10) | delete a slot. |
+---------------+--------------------------------------------------+
| Use Success | The server sends this event to the client on a |
| (=11) | successful connection. |
+---------------+--------------------------------------------------+
示例:廣播共享對象消息
此示例闡述了流創建過程中的消息交換,和共享對象的變換過程。同時還展示了共享對象消息廣播的處理過程。
.
+----------+ +----------+
| Client | | | Server |
+-----+----+ | +-----+----+
| Handshaking and Application |
| connect done |
| | |
| | |
| | |
| | |
Create and ---+---- |---- Shared Object Event(Use)---->|
connect | | |
Shared Object | | |
---+---- |<---- Shared Object Event---------|
| (UseSuccess,Clear) |
| |
---+---- |------ Shared Object Event ------>|
Shared object | | (RequestChange) |
Set Property | | |
---+---- |<------ Shared Object Event ------|
| (Success) |
| |
---+---- |------- Shared Object Event ----->|
Shared object| | (SendMessage) |
Message | | |
Broadcast ---+---- |<------- Shared Object Event -----|
| (SendMessage) |
| |
| |
Shared object message broadcast
7. Audio Message (8)
Audio Message(音頻信息,Message Type ID = 8):音頻數據
8. Video Message (9)
Video Message(視頻信息,Message Type ID = 9):視頻數據
9. Aggregate Message (22)
Aggregate Message(聚集信息,Message Type ID = 22):多個 RTMP 子消息的集合。
集合消息由消息頭和消息內容組成。
消息內容由子消息組成,子消息由消息頭,消息數據,回放指針組成。
.
+---------+-------------------------+
| Header | Aggregate Message body |
+---------+-------------------------+
The Aggregate Message format
+--------+-------+---------+--------+-------+---------+ - - - -
|Header 0|Message|Back |Header 1|Message|Back |
| |Data 0 |Pointer 0| |Data 1 |Pointer 1|
+--------+-------+---------+--------+-------+---------+ - - - -
The Aggregate Message body format
集合消息的消息流 ID 覆蓋此消息內的子消息流的ID。
集合消息和第一個子消息的時間戳之間的偏移量,用來將子消息的時間戳處理為流的時間刻度。每個子消息的時間戳可以通過
添加偏移量來處理為正常的流時間。第一個子消息的時間戳應該和集合消息的時間戳相同,因此偏移量應該為零。
方向指針包含了以前的消息(包含頭信息)的大小。集合消息包含此字段,一是為了適配 FLV 文件格式,二是為了回放定位。
使用集合消息有如下幾種優勢:
- 塊流在一個塊內至多可以攜帶一條完整的消息。使用集合消息之后,不僅可以增加塊大小,同時還減少了發送的塊數
量; - 集合消息的子消息可以連續的存儲在內存中。當系統調用網絡發送數據時更高效。
10. User Control Message Events (4)
User Control Message Events(用戶控制消息,Message Type ID = 4):告知對方執行該信息中包含的用戶控制事件,比如
Stream Begin 事件告知對方流信息開始傳輸。和前面提到的協議控制信息(Protocol Control Message)不同,用戶控制消息
是在 RTMP 協議層的,而不是在 RTMP chunk 流協議層,這個很容易弄混。該信息在 chunk 流中發送時,Message Stream ID
= 0,Chunk Strean id = 2,Message type id = 4.
用戶控制消息應該使用 0 作為消息流 ID,當通過 RTMP 塊流發送此消息時,塊流 ID 為 2. RTMP 流中的用戶控制消息在接收
時立即生效,消息中的時間戳被忽略。
客戶端或服務器發送此消息用來通知對方用戶控制事件。此消息包含事件類型和事件數據。
.
+------------------------------+------------------------
| Event Type (16 bits) | Event Data
+------------------------------+-------------------------
Payload for the ‘User Control’ protocol message
用戶控制消息的前 2 個字節數據用來標識事件類型。事件類型后面是事件數據。事件數據字段是可變的。由於此消息是通過
RTMP 塊流層發送的,塊大小的最大值應該滿足在一個塊里包含此消息。
用戶控制事件支持如下類型:
+---------------+--------------------------------------------------+
| Event | Description |
+---------------+--------------------------------------------------+
|Stream Begin | The server sends this event to notify the client |
| (=0) | that a stream has become functional and can be |
| | used for communication. By default, this event |
| | is sent on ID 0 after the application connect |
| | command is successfully received from the |
| | client. The event data is 4-byte and represents |
| | the stream ID of the stream that became |
| | functional. |
+---------------+--------------------------------------------------+
| Stream EOF | The server sends this event to notify the client |
| (=1) | that the playback of data is over as requested |
| | on this stream. No more data is sent without |
| | issuing additional commands. The client discards |
| | the messages received for the stream. The |
| | 4 bytes of event data represent the ID of the |
| | stream on which playback has ended. |
+---------------+--------------------------------------------------+
| StreamDry | The server sends this event to notify the client |
| (=2) | that there is no more data on the stream. If the |
| | server does not detect any message for a time |
| | period, it can notify the subscribed clients |
| | that the stream is dry. The 4 bytes of event |
| | data represent the stream ID of the dry stream. |
+---------------+--------------------------------------------------+
| SetBuffer | The client sends this event to inform the server |
| Length (=3) | of the buffer size (in milliseconds) that is |
| | used to buffer any data coming over a stream. |
| | This event is sent before the server starts |
| | processing the stream. The first 4 bytes of the |
| | event data represent the stream ID and the next |
| | 4 bytes represent the buffer length, in |
| | milliseconds. |
+---------------+--------------------------------------------------+
| StreamIs | The server sends this event to notify the client |
| Recorded (=4) | that the stream is a recorded stream. The |
| | 4 bytes event data represent the stream ID of |
| | the recorded stream. |
+---------------+--------------------------------------------------+
| PingRequest | The server sends this event to test whether the |
| (=6) | client is reachable. Event data is a 4-byte |
| | timestamp, representing the local server time |
| | when the server dispatched the command. The |
| | client responds with PingResponse on receiving |
| | MsgPingRequest. |
+---------------+--------------------------------------------------+
| PingResponse | The client sends this event to the server in |
| (=7) | response to the ping request. The event data is |
| | a 4-byte timestamp, which was received with the |
| | PingRequest request. |
+---------------+--------------------------------------------------+