天天看點

流媒體之RTMP——RTMP協定分析

文章目錄

    • 一:Message
      • 1.1 Protocol Control Messages
        • 1.1.1 Set Chunk Size(1)
        • 1.1.2 Abort Message(2)
        • 1.1.3 Acknowledgement(3)
        • 1.1.4 Window Acknowledgement Size(5)
        • 1.1.5 Set Peer Bandwidth(6)
      • 1.2 Command Messages
        • 1.2.1 User Control Messages (4)
        • 1.2.2 Command Message (20, 17)
        • 1.2.3 Data Message (18, 15)
        • 1.2.4 Shared Object Message (19, 16)
        • 1.2.5 Audio Message (8)
        • 1.2.6 Video Message (9)
        • 1.2.7 Aggregate Message (22)
    • 二:Chunk
      • 2.1 Basic Header
      • 2.2 Message Header
        • 2.2.1 Type0:11 bytes
        • 2.2.2 Type1:7 bytes
        • 2.2.3 Type2: 3 bytes
        • 2.2.4 Type3:0 bytes
      • 2.3 Extended Timestamp
      • 2.4 Example
        • 2.4.1 Audio Messages
        • 2.4.2 Split Messages
    • 三:Handshake
      • 3.1 C0和S0格式
      • 3.2 C1和S1格式
      • 3.3 C2和S2格式
    • 四:Stream和Chunk的了解
      • 4.1 談談Stream ID
      • 4.2 談談Stream、Message和Chunk關系
    • 五:拆包群組包的完整示例
      • 5.1 無優化
      • 5.2 有優化

作者:一步(Reser)

日期:2019.10.11

一:Message

Message為應用層的抽象,實際發送是将message 拆分為chunk發送。

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type  |                Payload length                 |
|   (1 byte)    |                 (3 bytes)                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Timestamp                               |
|                       (4 bytes)                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                Stream ID                      |
|                (3 bytes)                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           
  • Message Type:包類型,根據功能劃分;
  • Stream ID:Message通道,推流時請求建立後由伺服器傳回。

1.1 Protocol Control Messages

1.1.1 Set Chunk Size(1)

預設大小為128 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|                     chunk size (31 bits)                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

1.1.2 Abort Message(2)

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       chunk stream id (32 bits)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

1.1.3 Acknowledgement(3)

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        sequence number (4 bytes)              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

1.1.4 Window Acknowledgement Size(5)

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   Acknowledgement Window size (4 bytes)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

1.1.5 Set Peer Bandwidth(6)

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   Acknowledgement Window size                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Limit Type   |
+-+-+-+-+-+-+-+-+
           

1.2 Command Messages

1.2.1 User Control Messages (4)

message stream ID=0

chunk stream ID=2

+------------------------------+-------------------------
|     Event Type (16 bits)     | Event Data
+------------------------------+-------------------------
           

1.2.2 Command Message (20, 17)

1.2.3 Data Message (18, 15)

1.2.4 Shared Object Message (19, 16)

1.2.5 Audio Message (8)

1.2.6 Video Message (9)

1.2.7 Aggregate Message (22)

二:Chunk

+--------------+----------------+--------------------+--------------+
| Basic Header | Message Header | Extended Timestamp |  Chunk Data  |
+--------------+----------------+--------------------+--------------+
|                                                    |
|<------------------- Chunk Header ----------------->|
           

2.1 Basic Header

IDs: 2~63
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt|   cs id   |
+-+-+-+-+-+-+-+-+

IDs: 64~319 (the second byte + 64)
 0               1
 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt|     0     |   cs id - 64  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

IDs: 64~65599 (((the third byte)*256 + (the second byte) + 64))
 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt|     1     |        cs id - 64             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           
  • fmt - message header的四種類型之一;
  • cs id - 表示範圍

    3~65599

    ;
  • 64~319

    可以使用2位元組或3位元組的頭部表示。

2.2 Message Header

隻有Type0的時間戳為絕對時間,其他類型的時間戳都為相對時間!

2.2.1 Type0: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 (cont)     |message type id| msg stream id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           message stream id (cont)            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  
           

2.2.2 Type1: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 (cont)     |message type id|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

2.2.3 Type2: 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                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

2.2.4 Type3:0 bytes

++
||
++
           
timestamp >= 0xFFFFFF 時意味着使用擴充時間戳。

2.3 Extended Timestamp

當Message Header的時間戳為 0xFFFFFF 時使用擴充時間戳。4 bytes表示timestamp或timestamp delta。

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      extended timestamp                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

2.4 Example

2.4.1 Audio Messages

例如有以下音頻幀(Messages)需要發送:

+---------+-----------------+-----------------+-----------------+
|         |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    |
+---------+-----------------+-----------------+-------+---------+
           

因為都是音頻幀,是以走的Stream ID和Message Type ID相同。每幀的時間間隔為20ms,每幀長度相等。使用Chunk類型優化,最終最優的發送方案為:

+--------+---------+-----+------------+------- ---+------------+
|        | 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)     |           |            |
+--------+---------+-----+------------+-----------+------------+
           
  • 第一幀:因為沒有上一幀,是以Chunk無法優化,使用Chunk Type1;
  • 第二幀:和第一幀相比消息類型、資料長度等都相同,隻有時間戳不同,是以可以采用Chunk Type2對幀優化,頭部隻需timestamp delta字段;
  • 第三幀和第四幀:與上面幀相比,不單消息通道類型、資料長度等都相同,時間戳內插補點也都相同,為20,是以使用Chunk Type3,頭部無需任何資訊。

可見使用Chunk Type的優化還是很有效果的。

2.4.2 Split Messages

當Chunk Size=128時,如果Message長度過長就需要分割為多個Chunk。如有以下Messages:

+-----------+-------------------+-----------------+-----------------+
|           | Message Stream ID | Message TYpe ID | Time  | Length  |
+-----------+-------------------+-----------------+-----------------+
| Msg # 1   |       12346       |    9 (video)    | 1000  |   307   |
+-----------+-------------------+-----------------+-----------------+
           

Message的長度為307 > 128,是以需要對Message進行分割:

+-------+------+-----+-------------+-----------+------------+
|       |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)      |           |            |
+-------+------+-----+-------------+-----------+------------+
           

307 = 128 + 128 + 51。

可見當Chunk Type3的上一幀為Chunk Type2或者Chunk Type1時表示timestamp delta相同進行的優化,而如果上一幀為Chunk Type0則表示同一幀的分割。(此處可繼續優化)

三:Handshake

+-------------+                           +-------------+
         |    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
               |                    |                     |
           

其實整個握手過程涉及兩條握手線路,一條針對用戶端,一條針對服務端。

用戶端:

C1    ->    
      <-     S2            
           

服務端:

C0    ->     
      <-     S0/S1
C2    ->
           

3.1 C0和S0格式

0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|   version     |
+-+-+-+-+-+-+-+-+
           

C0中version表示用戶端請求的版本,S0的version表示server選擇的版本。

0~2

:廢棄

3

:目前使用版本

4~31

:保留

32~255

:禁止使用

3.2 C1和S1格式

1536 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        time (4 bytes)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        zero (4 bytes)/FMS Version             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random bytes                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         random bytes                          |
|                            (cont)                             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

3.3 C2和S2格式

1536 bytes,基本上是對C1和S1的回複。

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        time (4 bytes)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       time2 (4 bytes)                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        random echo                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         random echo                           |
|                            (cont)                             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

四:Stream和Chunk的了解

Message Stream為建立的一條資料通道,可能有以下三種:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       視訊/音頻                                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       隻含視訊                                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       隻含音頻                                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           

Message Stream由

Message Stream ID

唯一辨別。建立Message Stream通道之後就可以在通道中發送Message,Message的類型由

Message Stream Type

辨別。RTMP子產品在底層将對Message分割,分割的Chunk是真正發送的格式。

  • Chunk Type

    優化 - 同一個Message分割内部或者連續發送的Message之間;
  • Chunk ID

    - 同一個Message分割成的Chunks的

    Chunk ID

    相同;不同Message可以選擇使用相同的

    Chunk ID

    。官方文檔對ChunkID的使用描述很模糊,很大程度上會産生誤導。其實,隻要保證同一Message分割的Chunks的Chunk ID相同就可以了,這樣伺服器就可正确組包;此外,還要保證特定的Message使用特定的

    Chunk ID

    常用的Chunk ID有:

    • 2: Protocol Control Messages (1,2,3,5,6) & User Control Messages Event (4),Ping 和ByteRead通道
    • 3: Invoke通道,這個通道适用的消息很多,比較靈活, connect, create_stream, release_stream, delete_stream, fcpublish, fcunpublish, publish, play, pause, seek, send_get_stream_length, 以及script腳本資料
    • 4:Audio和Vidio通道
    • 5 、6 、7:伺服器保留,經觀察FMS2用這些Channel也用來發送音頻或視訊資料。
    // https://github.com/ossrs/srs/blob/master/trunk/src/kernel/srs_kernel_flv.hpp
    /**
     * the chunk stream id used for some under-layer message,
     * for example, the PC(protocol control) message.
     */
    #define RTMP_CID_ProtocolControl                0x02
    /**
     * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection.
     * generally use 0x03.
     */
    #define RTMP_CID_OverConnection                 0x03
    /**
     * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection,
     * the midst state(we guess).
     * rarely used, e.g. onStatus(NetStream.Play.Reset).
     */
    #define RTMP_CID_OverConnection2                0x04
    /**
     * the stream message(amf0/amf3), over NetStream.
     * generally use 0x05.
     */
    #define RTMP_CID_OverStream                     0x05
    /**
     * the stream message(amf0/amf3), over NetStream, the midst state(we guess).
     * rarely used, e.g. play("mp4:mystram.f4v")
     */
    #define RTMP_CID_OverStream2                    0x08
    /**
     * the stream message(video), over NetStream
     * generally use 0x06.
     */
    #define RTMP_CID_Video                          0x06
    /**
     * the stream message(audio), over NetStream.
     * generally use 0x07.
     */
    #define RTMP_CID_Audio                          0x07
               

4.1 談談Stream ID

這節很重要!

Message Header中的

Message Stream ID

到底是什麼?相信研究過RTMP協定的人十有八九都想搞清楚,但翻來覆去好像都沒有說明白的。

網上流行的兩種說法:

message stream id的位元組序是小端序,這個字段是為了解複用而設計的,RTMP文檔上說的相當的模糊。

message stream ID可以使任意值,不同的消息流複用成相同的chunk stream,基于它們的ID能夠解複用。于chunk stream 是相關的,這個字段是一個不透明的值沒有整明白什麼意思,我的了解就是用來辨別和伺服器連接配接的flash端的序号。
           

這種說法乍看相當合理,說Stream ID為用戶端辨別,所有的C/S指令處理直接通過鑒别這個辨別就可以了,無需再關注什麼IP之類。但測試發現,多個用戶端時候,伺服器傳回的

Message Stream ID

都相同,等于1。可見這個并不是針對不同用戶端的辨別,Adobe的人确實不按正常出牌。

另外說法:

StreamID占用RTMP標頭的最後4個位元組,是一個big-endian的int型資料。我們x86 計算機記憶體中資料存放都是小尾數模式:little-endian,而網絡資料流一般都是大尾數模式:big-endian。 StreamID是音視訊流的唯一ID, 一路流如果既有音頻包又有視訊包,那麼這路流音頻包的StreamID和他視訊包的StreamID相同,但ChannelID不同。
ChannelID 和StreamID之間的計算公式:StreamID=(ChannelID-4)/5+1  參考red5。如果這個封包既不是音頻包,也不是視訊包,那麼他的StreamID=0。例如當音視訊包ChannelID為2、3、4時StreamID都為1 當音視訊包ChannelID為9的時候StreamID為2。
           

簡言之,非音視訊的StreamID=0;音視訊的StreamID唯一。另外, S t r e a m I D = ( C h a n n e l I D − 4 ) / 5 + 1 StreamID=(ChannelID-4)/5+1 StreamID=(ChannelID−4)/5+1 ,這個可能是什麼?下文說明。

不妨再扒扒官方文檔 rtmp_specification_1.0.pdf :

7.2. Types of Commands
...
The following class objects are used to send various commands:

NetConnection An object that is a higher-level representation of
connection between the server and the client.
    
NetStream An object that represents the channel over which audio
streams, video streams and other related data are sent. We also
send commands like play , pause etc. which control the flow of the
data.
           

這裡好像說了兩個Object:

NetConnection

是C/S連接配接的高層控制協定;

NetStream

是媒體資料傳輸和媒體資料内容控制協定。繼續:

7.2.1. NetConnection Commands
The NetConnection manages a two-way connection between a client
application and the server. In addition, it provides support for
asynchronous remote method calls.
    
The following commands can be sent on the NetConnection :
o connect
o call
o close
o createStream
           

順便看下

createStream

7.2.1.3. createStream
The client sends this command to the server to create a logical
channel for message communication The publishing of audio, video, and
metadata is carried out over stream channel created using the
createStream command.
    
NetConnection is the default communication channel, which has a
stream ID 0. Protocol and a few command messages, including
createStream, use the default communication channel.
           

再看:

7.2.2. NetStream Commands
The NetStream defines the channel through which the streaming audio,
video, and data messages can flow over the NetConnection that
connects the client to the server. A NetConnection object can
support multiple NetStreams for multiple data streams.
    
The following commands can be sent on the NetStream by the client to
the server:
o play
o play2
o deleteStream
o closeStream
o receiveAudio
o receiveVideo
o publish
o seek
o pause
           

到此基本清晰:

  • RTMP的指令由兩類Object管理:

    NetConnection

    NetStream

  • NetConnection

    負責用戶端和服務端連接配接的高層管理,且具有預設的 S t r e a m I D = 0 StreamID=0 StreamID=0 ;
  • NetStream

    負責音視訊流資料的發送接收,以及流内容控制的一些指令;一個RTMP連接配接可以建立多個

    NetStream

    負責傳輸音視訊,每個通道自然也就會有自己的唯一StreamID;
  • 簡言之,

    NetConnection

    NetStream

    是一對多關系;但由于絕大多數情況下并不需要多個

    NetStream

    作為音視訊載體,是以通常為:

    NetConnection: StreamID=0

    NetStream: StreamID=1

    ;這也就解釋了為了伺服器傳回的 StreamID 通常為1了。

另外,librtmp源碼中

RTMP_SendCtrl

注釋裡有這樣一段描述:

/*
from http://jira.red5.org/confluence/display/docs/Ping:

Ping is the most mysterious message in RTMP and till now we haven't fully interpreted it yet. In summary, Ping message is used as a special command that are exchanged between client and server. This page aims to document all known Ping messages. Expect the list to grow.

The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters. The first parameter is the type of Ping and in short integer. The second parameter is the target of the ping. As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0 which means the Connection object, it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to. The second parameter takes this responsibility. The value has the same meaning as the target object field in RTMP header. (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.) The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet. Below is an unexhausted list of Ping messages.

    * type 0: Clear the stream. No third and fourth parameters. The second parameter could be 0. After the connection is established, a Ping 0,0 will be sent from server to client. The message will also be sent to client on the start of Play and in response of a Seek or Pause/Resume request. This Ping tells client to re-calibrate the clock with the timestamp of the next packet server sends.
    * type 1: Tell the stream to clear the playing buffer.
    * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
    * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
    * type 6: Ping the client from server. The second parameter is the current time.
    * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
    * type 26: SWFVerification request
    * type 27: SWFVerification response
*/
int
RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
{
    ...
}
           

這裡提到

nObject

參數和RTMP頭部的StreamID字段意義相同,都是指明額外的發送目标;而通常

nObject

的指派正是Server傳回的StreamID。

S t r e a m I D = ( C h a n n e l I D − 4 ) / 5 + 1 StreamID=(ChannelID-4)/5+1 StreamID=(ChannelID−4)/5+1 ,這個可能是什麼?當有多個

NetStream

時,為了友善Chunk的分包管理,對ChunkID和StreamID進行了分組對應,但具體實作也并不可知,也無需關注。

文檔中粗列了NetConnection和NetStream的适用指令,但還很多指令并沒有完全列出;此外,librtmp在指令發送時也并沒有嚴格按照文檔規範去填充 StreamID (m_nInfoField2字段),常常為0,但并沒有影響,不知如果有多路NetStream會不會出狀況。

4.2 談談Stream、Message和Chunk關系

這節也很重要!

Stream是什麼由上節已經比較清楚,其實就是C/S資料互動的邏輯通道,由StreamID辨別。而Message是針對包的邏輯概念,發送的一幀視訊,一幀音頻,一個控制指令,其實都是Message,由Message Type辨別。但這些Message并不是直接發送,而是在底層按照RTMP協定規範拆成了一個一個Chunk發送,這些Chunk在傳輸時會被決定走哪個Channel(虛拟通道),會被賦予Chunk ID(也可以認為是Channel ID),這個ChunkID按照Message類型賦予就可以了。

五:拆包群組包的完整示例

當ChunkSize=128時,考慮在Stream ID=12345的通道上傳輸長度為1024的四幀音頻資料(音頻的Type ID=8):

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-fkkZ5Bdj-1570787194095)(./raw.jpg)]

5.1 無優化

當無憂化時,四幀采用相同的發送方式:

流媒體之RTMP——RTMP協定分析

5.2 有優化

第一幀發送:

流媒體之RTMP——RTMP協定分析

第二幀發送:

流媒體之RTMP——RTMP協定分析

第三幀和第四幀發送:

流媒體之RTMP——RTMP協定分析

繼續閱讀