rtmp信令格式

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数据