FFmpeg

rtmp协议详解

2019-10-21  本文已影响0人  ZebraWei

版权声明:本文为卫伟学习总结文章,转载请注明出处!

1.handshake
1.1.概述

rtmp连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为c0,c1,c2;服务端发送的三个块命名为S0,S1,S2。
握手序列:

握手示意图
1.2.complex handshake
1.2.1 C0和S0格式
C0和S0包由一个字节组成,下面是C0/S0包内的字段:
1.2.2 C1和S1格式
C1和S1包含两部分数据: key和digest,分别为如下: key 和 digest 的顺序是不确定的,也有可能是:(nginx-rtmp中是如下的顺序):
764 bytes key 结构:
random-data: (offset) bytes

764 bytes digest 结构:

1.2.3 C2和S2格式
hanshake:S0 + S1 + S2

1.3 simple handshake

1.3.1 C0和S0格式

version(1byte):版本。在C0包内,这个字段代表客户端请求的RTMP版本号。在S0包内,这个字段代表服务端选择的RTMP版本号。当前使用的版本是3。在版本0-2用在早期的产品中,如今已经弃用;版本4-31被预留用于后续产品;版本32-255(为了区分RTMAP协议和文本协议,文本协议通常是可以打印字符)不允许使用。如果服务端无法识别客户端的版本号,应该回复版本3。客户端可以选择降低到版本3,或者终止握手过程。

1.3.2 C1和S1格式
C1和S1包长度为1536字节,包含以下字段:
1.3.3 C2和S2格式
C2 和 S2 包长度为 1536 字节,作为 C1 和 S1 的回应,包含以下字段:
2.组块
2.1块格式
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: 1byte
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。 1571629877(1).jpg
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.
2.3 Message Header

包含了要发送的实际消息(可能是完整的,也可能是一部分)的描述消息。Message Header的格式和长度取决于Basic Header的chunk type,即fmt,共有四种不同的格式。其中一种格式可以表示其他三种表示的所有数据,但由于其他三种格式是基于对之前chunk的差量化的表示,因此可以更简洁地表示相同的数据,实际使用的时候还是应该采用尽量少的字节表示相同意义的数据。下面按字节从多到少的顺序分别介绍这四种格式的 Message Header。

Message Header四种消息头格式
一、Chunk Type(fmt)=0:11bytes

type=0时Message Header占用11个字节,其他三种能表示的数据它都能表示,但Chunk stream的开始第一个chunk和头信息中时间戳后退(即值与上一个chunk相比减少,通常在回退播放的时候会出现这种情况)的时候必须采用这种格式。

二、Chunk Type(fmt)=1:7bytes

type为1时占用7个字节,省去了表示message stream id的4个字节,表示此chunk和上一次发的chunk所在的流相同,如果在发送端和对端有一个流链接的时候可以尽量采用这种格式。

三、Chunk Type(fmt)=2: 3 bytes

type 为 2 时占用 3 个字节,相对于 type = 1 格式又省去了表示消息长度的3个字节和表示消息类型的1个字节,表示此 chunk和上一次发送的 chunk 所在的流、消息的长度和消息的类型都相同。余下的这三个字节表示 timestamp delta,使用同type=1。

四、Chunk Type(fmt)=3: 0byte

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
本示例展示了一个音频消息流。流中包含有冗余信息。

最后实际发送的chunk如下面表格所示,该表格展示了由此音频流产生的块信息。从第 3 条信息开始,数据传输达到最大优化。每条消息的头部只增加了 1 字节长度。

上一篇 下一篇

猜你喜欢

热点阅读