概要
- 在RTMP协议中信令和媒体数据都称之为Message,在网络中传输这些Message,为了区分它们肯定是要加一个Message head的,所以RTMP协议也有一个Message head,还有一个问题因为RTMP协议是基于TCP的,由于TCP的包长度是有限制的(一般来说不超过1500个字节),而RTMP的Message长度是有可能很大的,像一个视频帧的包可能会有几十甚至几千K,这个问题就必然有一个分片的问题,在RTMP协议中对应的说法就是chunk,每一个Message + head都是由一个和多个chunk组成的。到这里对RTMP协议的概要理解就算完了。
- RTMP在收发数据的时候并不是以Message为单位的,而是把Message拆分成Chunk发送
- 每个Chunk中带有MessageID代表属于哪个Message,接受端也会按照这个id来将chunk组装成Message
- Chunk的默认大小是128字节,在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接受端会各自维护一个Chunk Size,可以分别设置这个值来改变自己这一方发送的Chunk的最大大小。
- csid, chunk stream ID
head
- RTMP的head在协议中的表现形式是chunk head,前面已经说到一个Message + head可以分成一个和多个chunk,为了区分这些chunk,肯定是需要一个chunk head的,具体的实现就把Message head的信息和chunk head的信息合并在一起以chunk head的形式表现。
- 一个完整的chunk的组成如下图所示
Basic Header | Chunk Msg Header | Extend TimeStamp | Chunk Data |
---|
-
说明
-
Chunk basic header:该字段包含chunk的stream ID和type。chunk的Type决定了消息头的编码方式。该字段的长度完全依赖于stream ID,该字段是一个可变长的字段.chunk basic head的长度为1~3个字节,具体长度主要是依赖chunk stream ID的长度,所谓chunk stream ID是flash server用来管理连接的客户端的信令交互的标识,在red5的文档中称之为channel ID,协议最大支持65597个streamID 从3~65599。ID 0,1,2为协议保留,0代表ID是64~319(第二个byte + 64);1代表chunk stream ID为64~65599((第三个byte)* 256 + 第二个byte + 64)(小端表示);2代表该消息为低层的协议(在RTMP协议中控制信令的chunk stream ID都是2)。3~63的chunk stream ID就是该byte的值。没有附加的字段来标识chunk stream streamID。在这里要指出的是虽然RTMP的chunk stream ID理论是可以达到65599,但是目前使用的chunk stream ID很少,2~7都是约定的,8是用来传输publish play等命令,其他的chunk stream ID目前好像没有使用,至少我不知道用来干嘛的。
所以目前chunk basic head的长度一般为1个字节。这一个字节由两部分组成fmt (2bit) cs id fmt是chunk type,fmt占两个bit用来标识紧跟其后的chunk Msg Header的长度,cs id占六个bit。两位的fmt取值为 0~3,分别代表的意义如下:
case 0:chunk Msg Header长度为11;
case 1:chunk Msg Header长度为7;
case 2:chunk Msg Header长度为3;
case 3:chunk Msg Header长度为0;
所以 只有一个字节的chunk basic header取值为 chunk basic header = (fmt << 6) | (cs id). -
Chunk Msg Header:0, 3 ,7,11,该字段包含了将要发送的消息的信息(或者是一部分,一个消息拆成多个chunk的情况下是一部分)该字段的长度由chunk basic header中的type决定。Chunk Msg Header的长度是可变的,Chunk Msg Header可变的原因是为了压缩传输的字节数,把一些相同类型的chunk的head去掉一些字节,换句话说就是四种类型的包头都可以通过一定的规则还原成11个字节,这个压缩和还原在RTMP协议中称之为复用/解复用。
那我们以11个字节的完整包头来解释Chunk Msg Header,如图所示
timestamp message length message type id message stream id -
Timestamp:3bytes
对于type 0的chunk,绝对时间戳在这里表示,如果时间戳值大于等于0xffffff(16777215),该值必须是0xffffff,且时间戳扩展字段必须发送,其他情况没有要求。-
message length:3bytes
Message的长度,注意这里的长度并不是跟随chunk head其后的chunk data(Payload)的长度,而是前文提到的一条信令或者一帧视频数据或音频数据的长度。前文提到过信令或者媒体数据都称之为Message,一条Message可以分为一条或者多条chunk。 -
message type id:1byte. Message的类型ID
-
message stream id:4bytes. message stream id的字节序是小端序,这个字段是为了解复用而设计的,RTMP文档上说的相当的模糊,message stream ID可以使任意值,不同的消息流复用成相同的chunk stream,基于它们的ID能够解复用。于chunk stream 是相关的,这个字段是一个不透明的值没有整明白什么意思,我的理解就是用来标识和服务器连接的flash端的序号。长度是7 bytes 的chunk head,该类型不包含stream ID,该chunk的streamID和前一个chunk的stream ID是相同的,变长的消息,例如视频流格式,在第一个新的chunk以后使用这种类型,注意其中时间戳部分是相对时间,为何上一个绝对时间之间的差值 如图所示:
timestamp delta message length message type id
-
-
Extend Timestamp: 0 ,4 bytes,该字段发送的时候必须是正常的时间戳设置成0xffffff时,当正常时间戳不为0xffffff时,该字段不发送。当时间戳比0xffffff小该字段不发送,当时间戳比0xffffff大时该字段必须发送,且正常时间戳设置成0xffffff。
-
Chunk Data : 实际数据(Payload),可以是信令,也可以是媒体数据。
-
图示
参考
- https://blog.csdn.net/caoshangpa/article/details/52872146
- https://www.villainhr.com/page/2017/08/05/RTMP H5 直播流技术解析
RTMP payload(body)解析
- 因为 RTMP 是 Adobe 开发的。理所当然,内部的使用格式肯定是 FLV 格式。
- 如果是传整个 FLV 文件,根本没必要用到这个,而且,RTMP 最常用在直播当中。直播中的视频都是分段播放的。综上所述,RTMP 是根据自己的自定义协议来分段传输 FLV Tag 的。那具体的协议是啥呢?
这个在 RTMP 官方文档中其实也没有给出。它只是告诉我们 Video Msg 的 type ID 是 9 而已。
FLV
FLV header
- FLV是一个二进制文件,简单来说,其是由一个文件头(FLV header)和很多tag组成(FLV body)。tag又可以分成三类:audio,video,script,分别代表音频流,视频流,脚本流,而每个tag又由tag header和tag data组成。
- 文件头由9bytes组成
- 文件头
字段 | 解释 | 大小 |
---|---|---|
文件头 | 总是“FLV” | 3bytes |
版本 | 一般是0x01 | 1bytes |
流信息 | 倒数第一bit是1表示有视频(0x01),倒数第三bit是1表示有音频(0x4),有视频又有音频就是0x01 | 1bytes |
header长度 | 最后4bytes表示FLV 头的长度,3+1+1+4 = 9。 | 4bytes |
- 前3个bytes是文件类型,总是“FLV”,也就是(0x46 0x4C 0x56)。第4btye是版本号,目前一般是0x01。第5byte是流的信息,倒数第一bit是1表示有视频(0x01),倒数第三bit是1表示有音频(0x4),有视频又有音频就是0x01 | 0x04(0x05),其他都应该是0。最后4bytes表示FLV 头的长度,3+1+1+4 = 9。
FLV body
- 基本结构
tag header | data |
---|
- tag header 结构
tag type(1) | length(3) | timestamp(3) | extend timestamp(1) | stream id(3) |
---|
- FLV header后面就是FLV body,FLV body由若干个tag组成。
- 每一个tag第一部分是tag header,tag header长度为11bytes,但是每个tag header前面有4bytes记录着上一个tag的长度,此待会儿再说。
- tag header的第1个byte为记录着tag的类型,音频(0x8),视频(0x9),脚本(0x12);
- 第2到4bytes是数据区的长度,也就是tag data的长度;
- 再后面3个bytes是时间戳,单位是毫秒,类型为0x12则时间戳为0,时间戳控制着文件播放的速度,可以根据音视频的帧率类设置;
- 时间戳后面一个byte是扩展时间戳,时间戳不够长的时候用;
- 最后3bytes是streamID,但是总为0,再后面就是数据区了(tagdata),也即是h264的裸流,tag header 长度为1+3+3+1+3=11。
tag data
音频数据
- tag data如果是音频数据,第一个byte记录audio信息
- 概览
FLV header | previous tag size0 | tag1 | previous tag size1 | tag2 | previous tag size2 | … | tagN | previous tag sizeN |
---|
- 基本结构
audio format(4) | sample rate(2) | 采样长度(1) | 类型(1) | 数据 |
---|
- 前4bits表示音频格式
值 | 含义 |
---|---|
0 | 未压缩 |
1 | ADPCM |
2 | MP3 |
4 | Nellymoser 16-kHz mono |
5 | Nellymoser 8-kHz mono |
10 | AAC |
- 下面两个bits表示samplerate
值 | 含义 |
---|---|
0 | 5.5KHz |
1 | 11kHz |
2 | 22kHz |
3 | 44kHz |
- 下面1bit表示采样长度
值 | 含义 |
---|---|
0 | 8Bit |
1 | 16Bit |
- 下面1bit表示类型
值 | 含义 |
---|---|
0 | sndMomo |
1 | sndStereo |
视频数据
- 基本结构
type(4) | codec id(4) | if( codec_id == 7) AVCPacketType | 数据 |
---|
- type
值 | 含义 |
---|---|
1 | keyframe |
2 | inner frame |
3 | disposable inner frame |
4 | generated keyframe |
- codec id
值 | 含义 |
---|---|
2 | seronson h.263 |
3 | screen video |
4 | On2 VP6 |
5 | On2 VP6 with alpha channel |
6 | Screen video version 2 |
7 | AVC (h.264) |